Merge "Finalize ENABLE_FEATURE_SCAN_APEX"
diff --git a/Android.bp b/Android.bp
index 0315c12..0a14565 100644
--- a/Android.bp
+++ b/Android.bp
@@ -214,6 +214,7 @@
         "android.hardware.radio-V1.5-java",
         "android.hardware.radio-V1.6-java",
         "android.hardware.radio.data-V1-java",
+        "android.hardware.radio.ims-V1-java",
         "android.hardware.radio.messaging-V1-java",
         "android.hardware.radio.modem-V1-java",
         "android.hardware.radio.network-V2-java",
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index ab20fdb..98e4f45 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -44,6 +44,8 @@
         "apct-perftests-utils",
         "collector-device-lib",
         "compatibility-device-util-axt",
+        "junit",
+        "junit-params",
         "core-tests-support",
         "guava",
     ],
diff --git a/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
index 3f4f6af..3ebaa4c 100644
--- a/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
@@ -20,18 +20,19 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class DeepArrayOpsPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -39,19 +40,14 @@
     private Object[] mArray;
     private Object[] mArray2;
 
-    @Parameterized.Parameter(0)
-    public int mArrayLength;
-
-    @Parameterized.Parameters(name = "mArrayLength({0})")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{1}, {4}, {16}, {32}, {2048}});
     }
 
-    @Before
-    public void setUp() throws Exception {
-        mArray = new Object[mArrayLength * 14];
-        mArray2 = new Object[mArrayLength * 14];
-        for (int i = 0; i < mArrayLength; i += 14) {
+    public void setUp(int arrayLength) throws Exception {
+        mArray = new Object[arrayLength * 14];
+        mArray2 = new Object[arrayLength * 14];
+        for (int i = 0; i < arrayLength; i += 14) {
             mArray[i] = new IntWrapper(i);
             mArray2[i] = new IntWrapper(i);
 
@@ -99,7 +95,9 @@
     }
 
     @Test
-    public void deepHashCode() {
+    @Parameters(method = "getData")
+    public void deepHashCode(int arrayLength) throws Exception {
+        setUp(arrayLength);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             Arrays.deepHashCode(mArray);
@@ -107,7 +105,9 @@
     }
 
     @Test
-    public void deepEquals() {
+    @Parameters(method = "getData")
+    public void deepEquals(int arrayLength) throws Exception {
+        setUp(arrayLength);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             Arrays.deepEquals(mArray, mArray2);
diff --git a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
index 5aacfc2..20f1309 100644
--- a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
@@ -20,22 +20,22 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class SystemArrayCopyPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "arrayLength={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {2}, {4}, {8}, {16}, {32}, {64}, {128}, {256}, {512}, {1024}, {2048}, {4096},
@@ -43,12 +43,10 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public int arrayLength;
-
     // Provides benchmarking for different types of arrays using the arraycopy function.
     @Test
-    public void timeSystemCharArrayCopy() {
+    @Parameters(method = "getData")
+    public void timeSystemCharArrayCopy(int arrayLength) {
         final int len = arrayLength;
         char[] src = new char[len];
         char[] dst = new char[len];
@@ -59,7 +57,8 @@
     }
 
     @Test
-    public void timeSystemByteArrayCopy() {
+    @Parameters(method = "getData")
+    public void timeSystemByteArrayCopy(int arrayLength) {
         final int len = arrayLength;
         byte[] src = new byte[len];
         byte[] dst = new byte[len];
@@ -70,7 +69,8 @@
     }
 
     @Test
-    public void timeSystemShortArrayCopy() {
+    @Parameters(method = "getData")
+    public void timeSystemShortArrayCopy(int arrayLength) {
         final int len = arrayLength;
         short[] src = new short[len];
         short[] dst = new short[len];
@@ -81,7 +81,8 @@
     }
 
     @Test
-    public void timeSystemIntArrayCopy() {
+    @Parameters(method = "getData")
+    public void timeSystemIntArrayCopy(int arrayLength) {
         final int len = arrayLength;
         int[] src = new int[len];
         int[] dst = new int[len];
@@ -92,7 +93,8 @@
     }
 
     @Test
-    public void timeSystemLongArrayCopy() {
+    @Parameters(method = "getData")
+    public void timeSystemLongArrayCopy(int arrayLength) {
         final int len = arrayLength;
         long[] src = new long[len];
         long[] dst = new long[len];
@@ -103,7 +105,8 @@
     }
 
     @Test
-    public void timeSystemFloatArrayCopy() {
+    @Parameters(method = "getData")
+    public void timeSystemFloatArrayCopy(int arrayLength) {
         final int len = arrayLength;
         float[] src = new float[len];
         float[] dst = new float[len];
@@ -114,7 +117,8 @@
     }
 
     @Test
-    public void timeSystemDoubleArrayCopy() {
+    @Parameters(method = "getData")
+    public void timeSystemDoubleArrayCopy(int arrayLength) {
         final int len = arrayLength;
         double[] src = new double[len];
         double[] dst = new double[len];
@@ -125,7 +129,8 @@
     }
 
     @Test
-    public void timeSystemBooleanArrayCopy() {
+    @Parameters(method = "getData")
+    public void timeSystemBooleanArrayCopy(int arrayLength) {
         final int len = arrayLength;
         boolean[] src = new boolean[len];
         boolean[] dst = new boolean[len];
diff --git a/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
index eec0734..b1b594d 100644
--- a/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
@@ -20,42 +20,32 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.CharArrayWriter;
 import java.lang.reflect.Constructor;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Random;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class XmlSerializePerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mDatasetAsString({0}), mSeed({1})")
-    public static Collection<Object[]> data() {
-        return Arrays.asList(
-                new Object[][] {
-                    {"0.99 0.7 0.7 0.7 0.7 0.7", 854328},
-                    {"0.999 0.3 0.3 0.95 0.9 0.9", 854328},
-                    {"0.99 0.7 0.7 0.7 0.7 0.7", 312547},
-                    {"0.999 0.3 0.3 0.95 0.9 0.9", 312547}
-                });
+    private Object[] getParams() {
+        return new Object[][] {
+            new Object[] {"0.99 0.7 0.7 0.7 0.7 0.7", 854328},
+            new Object[] {"0.999 0.3 0.3 0.95 0.9 0.9", 854328},
+            new Object[] {"0.99 0.7 0.7 0.7 0.7 0.7", 312547},
+            new Object[] {"0.999 0.3 0.3 0.95 0.9 0.9", 312547}
+        };
     }
 
-    @Parameterized.Parameter(0)
-    public String mDatasetAsString;
-
-    @Parameterized.Parameter(1)
-    public int mSeed;
-
     double[] mDataset;
     private Constructor<? extends XmlSerializer> mKxmlConstructor;
     private Constructor<? extends XmlSerializer> mFastConstructor;
@@ -100,8 +90,7 @@
     }
 
     @SuppressWarnings("unchecked")
-    @Before
-    public void setUp() throws Exception {
+    public void setUp(String datasetAsString) throws Exception {
         mKxmlConstructor =
                 (Constructor)
                         Class.forName("com.android.org.kxml2.io.KXmlSerializer").getConstructor();
@@ -109,28 +98,32 @@
                 (Constructor)
                         Class.forName("com.android.internal.util.FastXmlSerializer")
                                 .getConstructor();
-        String[] splitStrings = mDatasetAsString.split(" ");
+        String[] splitStrings = datasetAsString.split(" ");
         mDataset = new double[splitStrings.length];
         for (int i = 0; i < splitStrings.length; i++) {
             mDataset[i] = Double.parseDouble(splitStrings[i]);
         }
     }
 
-    private void internalTimeSerializer(Constructor<? extends XmlSerializer> ctor)
+    private void internalTimeSerializer(Constructor<? extends XmlSerializer> ctor, int seed)
             throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            serializeRandomXml(ctor, mSeed);
+            serializeRandomXml(ctor, seed);
         }
     }
 
     @Test
-    public void timeKxml() throws Exception {
-        internalTimeSerializer(mKxmlConstructor);
+    @Parameters(method = "getParams")
+    public void timeKxml(String datasetAsString, int seed) throws Exception {
+        setUp(datasetAsString);
+        internalTimeSerializer(mKxmlConstructor, seed);
     }
 
     @Test
-    public void timeFast() throws Exception {
-        internalTimeSerializer(mFastConstructor);
+    @Parameters(method = "getParams")
+    public void timeFast(String datasetAsString, int seed) throws Exception {
+        setUp(datasetAsString);
+        internalTimeSerializer(mFastConstructor, seed);
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
index 31c92ba..3a45d40 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -20,12 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -38,23 +38,18 @@
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class ZipFilePerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
     private File mFile;
 
-    @Parameters(name = "numEntries={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{128}, {1024}, {8192}});
     }
 
-    @Parameterized.Parameter(0)
-    public int numEntries;
-
-    @Before
-    public void setUp() throws Exception {
+    public void setUp(int numEntries) throws Exception {
         mFile = File.createTempFile(getClass().getName(), ".zip");
         mFile.deleteOnExit();
         writeEntries(new ZipOutputStream(new FileOutputStream(mFile)), numEntries, 0);
@@ -66,7 +61,9 @@
     }
 
     @Test
-    public void timeZipFileOpen() throws Exception {
+    @Parameters(method = "getData")
+    public void timeZipFileOpen(int numEntries) throws Exception {
+        setUp(numEntries);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             ZipFile zf = new ZipFile(mFile);
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
index faa9628..2e89518 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
@@ -20,12 +20,13 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -39,21 +40,17 @@
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class ZipFileReadPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "readBufferSize={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{1024}, {16384}, {65536}});
     }
 
     private File mFile;
 
-    @Parameterized.Parameter(0)
-    public int readBufferSize;
-
     @Before
     public void setUp() throws Exception {
         mFile = File.createTempFile(getClass().getName(), ".zip");
@@ -90,7 +87,8 @@
     }
 
     @Test
-    public void timeZipFileRead() throws Exception {
+    @Parameters(method = "getData")
+    public void timeZipFileRead(int readBufferSize) throws Exception {
         byte[] readBuffer = new byte[readBufferSize];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java
index db5462c..2c0473e 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java
@@ -20,96 +20,99 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class BitSetPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mSize={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{1000}, {10000}});
     }
 
-    @Parameterized.Parameter(0)
-    public int mSize;
-
-    private BitSet mBitSet;
-
-    @Before
-    public void setUp() throws Exception {
-        mBitSet = new BitSet(mSize);
-    }
-
     @Test
-    public void timeIsEmptyTrue() {
+    @Parameters(method = "getData")
+    public void timeIsEmptyTrue(int size) {
+        BitSet bitSet = new BitSet(size);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            if (!mBitSet.isEmpty()) throw new RuntimeException();
+            if (!bitSet.isEmpty()) throw new RuntimeException();
         }
     }
 
     @Test
-    public void timeIsEmptyFalse() {
-        mBitSet.set(mBitSet.size() - 1);
+    @Parameters(method = "getData")
+    public void timeIsEmptyFalse(int size) {
+        BitSet bitSet = new BitSet(size);
+        bitSet.set(bitSet.size() - 1);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            if (mBitSet.isEmpty()) throw new RuntimeException();
+            if (bitSet.isEmpty()) throw new RuntimeException();
         }
     }
 
     @Test
-    public void timeGet() {
+    @Parameters(method = "getData")
+    public void timeGet(int size) {
+        BitSet bitSet = new BitSet(size);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         int i = 1;
         while (state.keepRunning()) {
-            mBitSet.get(++i % mSize);
+            bitSet.get(++i % size);
         }
     }
 
     @Test
-    public void timeClear() {
+    @Parameters(method = "getData")
+    public void timeClear(int size) {
+        BitSet bitSet = new BitSet(size);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         int i = 1;
         while (state.keepRunning()) {
-            mBitSet.clear(++i % mSize);
+            bitSet.clear(++i % size);
         }
     }
 
     @Test
-    public void timeSet() {
+    @Parameters(method = "getData")
+    public void timeSet(int size) {
+        BitSet bitSet = new BitSet(size);
         int i = 1;
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mBitSet.set(++i % mSize);
+            bitSet.set(++i % size);
         }
     }
 
     @Test
-    public void timeSetOn() {
+    @Parameters(method = "getData")
+    public void timeSetOn(int size) {
+        BitSet bitSet = new BitSet(size);
         int i = 1;
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mBitSet.set(++i % mSize, true);
+            bitSet.set(++i % size, true);
         }
     }
 
     @Test
-    public void timeSetOff() {
+    @Parameters(method = "getData")
+    public void timeSetOff(int size) {
+        BitSet bitSet = new BitSet(size);
         int i = 1;
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mBitSet.set(++i % mSize, false);
+            bitSet.set(++i % size, false);
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java
index 3952c12..6a2ce58 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java
@@ -20,18 +20,19 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.text.BreakIterator;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Locale;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class BreakIteratorPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -41,36 +42,37 @@
                 Locale.US,
                 "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi mollis consequat"
                     + " nisl non pharetra. Praesent pretium vehicula odio sed ultrices. Aenean a"
-                    + " felis libero. Vivamus sed commodo nibh. Pellentesque turpis lectus, euismod"
-                    + " vel ante nec, cursus posuere orci. Suspendisse velit neque, fermentum"
-                    + " luctus ultrices in, ultrices vitae arcu. Duis tincidunt cursus lorem. Nam"
-                    + " ultricies accumsan quam vitae imperdiet. Pellentesque habitant morbi"
-                    + " tristique senectus et netus et malesuada fames ac turpis egestas. Quisque"
-                    + " aliquet pretium nisi, eget laoreet enim molestie sit amet. Class aptent"
-                    + " taciti sociosqu ad litora torquent per conubia nostra, per inceptos"
+                    + " felis libero. Vivamus sed commodo nibh. Pellentesque turpis lectus,"
+                    + " euismod vel ante nec, cursus posuere orci. Suspendisse velit neque,"
+                    + " fermentum luctus ultrices in, ultrices vitae arcu. Duis tincidunt cursus"
+                    + " lorem. Nam ultricies accumsan quam vitae imperdiet. Pellentesque habitant"
+                    + " morbi tristique senectus et netus et malesuada fames ac turpis egestas."
+                    + " Quisque aliquet pretium nisi, eget laoreet enim molestie sit amet. Class"
+                    + " aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos"
                     + " himenaeos.\n"
                     + "Nam dapibus aliquam lacus ac suscipit. Proin in nibh sit amet purus congue"
                     + " laoreet eget quis nisl. Morbi gravida dignissim justo, a venenatis ante"
-                    + " pulvinar at. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin"
-                    + " ultrices vestibulum dui, vel aliquam lacus aliquam quis. Duis fringilla"
-                    + " sapien ac lacus egestas, vel adipiscing elit euismod. Donec non tellus"
-                    + " odio. Donec gravida eu massa ac feugiat. Aliquam erat volutpat. Praesent id"
-                    + " adipiscing metus, nec laoreet enim. Aliquam vitae posuere turpis. Mauris ac"
-                    + " pharetra sem. In at placerat tortor. Vivamus ac vehicula neque. Cras"
-                    + " volutpat ullamcorper massa et varius. Praesent sagittis neque vitae nulla"
-                    + " euismod pharetra.\n"
+                    + " pulvinar at. Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+                    + " Proin ultrices vestibulum dui, vel aliquam lacus aliquam quis. Duis"
+                    + " fringilla sapien ac lacus egestas, vel adipiscing elit euismod. Donec non"
+                    + " tellus odio. Donec gravida eu massa ac feugiat. Aliquam erat volutpat."
+                    + " Praesent id adipiscing metus, nec laoreet enim. Aliquam vitae posuere"
+                    + " turpis. Mauris ac pharetra sem. In at placerat tortor. Vivamus ac vehicula"
+                    + " neque. Cras volutpat ullamcorper massa et varius. Praesent sagittis neque"
+                    + " vitae nulla euismod pharetra.\n"
                     + "Sed placerat sapien non molestie sollicitudin. Nullam sit amet dictum quam."
                     + " Etiam tincidunt tortor vel pretium vehicula. Praesent fringilla ipsum vel"
                     + " velit luctus dignissim. Nulla massa ligula, mattis in enim et, mattis"
                     + " lacinia odio. Suspendisse tristique urna a orci commodo tempor. Duis"
                     + " lacinia egestas arcu a sollicitudin.\n"
                     + "In ac feugiat lacus. Nunc fermentum eu est at tristique. Pellentesque quis"
-                    + " ligula et orci placerat lacinia. Maecenas quis mauris diam. Etiam mi ipsum,"
-                    + " tempus in purus quis, euismod faucibus orci. Nulla facilisi. Praesent sit"
-                    + " amet sapien vel elit porta adipiscing. Phasellus sit amet volutpat diam.\n"
-                    + "Proin bibendum elit non lacus pharetra, quis eleifend tellus placerat. Nulla"
-                    + " facilisi. Maecenas ante diam, pellentesque mattis mattis in, porta ut"
-                    + " lorem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices"
+                    + " ligula et orci placerat lacinia. Maecenas quis mauris diam. Etiam mi"
+                    + " ipsum, tempus in purus quis, euismod faucibus orci. Nulla facilisi."
+                    + " Praesent sit amet sapien vel elit porta adipiscing. Phasellus sit amet"
+                    + " volutpat diam.\n"
+                    + "Proin bibendum elit non lacus pharetra, quis eleifend tellus placerat."
+                    + " Nulla facilisi. Maecenas ante diam, pellentesque mattis mattis in, porta"
+                    + " ut lorem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices"
                     + " posuere cubilia Curae; Nunc interdum tristique metus, in scelerisque odio"
                     + " fermentum eget. Cras nec venenatis lacus. Aenean euismod eget metus quis"
                     + " molestie. Cras tincidunt dolor ut massa ornare, in elementum lacus auctor."
@@ -80,29 +82,29 @@
         LONGPARA(
                 Locale.US,
                 "During dinner, Mr. Bennet scarcely spoke at all; but when the servants were"
-                    + " withdrawn, he thought it time to have some conversation with his guest, and"
-                    + " therefore started a subject in which he expected him to shine, by observing"
-                    + " that he seemed very fortunate in his patroness. Lady Catherine de Bourgh's"
-                    + " attention to his wishes, and consideration for his comfort, appeared very"
-                    + " remarkable. Mr. Bennet could not have chosen better. Mr. Collins was"
-                    + " eloquent in her praise. The subject elevated him to more than usual"
-                    + " solemnity of manner, and with a most important aspect he protested that"
-                    + " \"he had never in his life witnessed such behaviour in a person of"
-                    + " rank--such affability and condescension, as he had himself experienced from"
-                    + " Lady Catherine. She had been graciously pleased to approve of both of the"
-                    + " discourses which he had already had the honour of preaching before her. She"
-                    + " had also asked him twice to dine at Rosings, and had sent for him only the"
-                    + " Saturday before, to make up her pool of quadrille in the evening. Lady"
-                    + " Catherine was reckoned proud by many people he knew, but _he_ had never"
-                    + " seen anything but affability in her. She had always spoken to him as she"
-                    + " would to any other gentleman; she made not the smallest objection to his"
-                    + " joining in the society of the neighbourhood nor to his leaving the parish"
-                    + " occasionally for a week or two, to visit his relations. She had even"
-                    + " condescended to advise him to marry as soon as he could, provided he chose"
-                    + " with discretion; and had once paid him a visit in his humble parsonage,"
-                    + " where she had perfectly approved all the alterations he had been making,"
-                    + " and had even vouchsafed to suggest some herself--some shelves in the closet"
-                    + " up stairs.\""),
+                    + " withdrawn, he thought it time to have some conversation with his guest,"
+                    + " and therefore started a subject in which he expected him to shine, by"
+                    + " observing that he seemed very fortunate in his patroness. Lady Catherine"
+                    + " de Bourgh's attention to his wishes, and consideration for his comfort,"
+                    + " appeared very remarkable. Mr. Bennet could not have chosen better. Mr."
+                    + " Collins was eloquent in her praise. The subject elevated him to more than"
+                    + " usual solemnity of manner, and with a most important aspect he protested"
+                    + " that \"he had never in his life witnessed such behaviour in a person of"
+                    + " rank--such affability and condescension, as he had himself experienced"
+                    + " from Lady Catherine. She had been graciously pleased to approve of both of"
+                    + " the discourses which he had already had the honour of preaching before"
+                    + " her. She had also asked him twice to dine at Rosings, and had sent for him"
+                    + " only the Saturday before, to make up her pool of quadrille in the evening."
+                    + " Lady Catherine was reckoned proud by many people he knew, but _he_ had"
+                    + " never seen anything but affability in her. She had always spoken to him as"
+                    + " she would to any other gentleman; she made not the smallest objection to"
+                    + " his joining in the society of the neighbourhood nor to his leaving the"
+                    + " parish occasionally for a week or two, to visit his relations. She had"
+                    + " even condescended to advise him to marry as soon as he could, provided he"
+                    + " chose with discretion; and had once paid him a visit in his humble"
+                    + " parsonage, where she had perfectly approved all the alterations he had"
+                    + " been making, and had even vouchsafed to suggest some herself--some shelves"
+                    + " in the closet up stairs.\""),
         GERMAN(
                 Locale.GERMANY,
                 "Aber dieser Freiheit setzte endlich der Winter ein Ziel. Draußen auf den Feldern"
@@ -119,15 +121,14 @@
                     + " เดิมทีเป็นการผสมผสานกันระหว่างสำเนียงอยุธยาและชาวไทยเชื้อสายจีนรุ่นหลังที่"
                     + "พูดไทยแทนกลุ่มภาษาจีน"
                     + " ลักษณะเด่นคือมีการออกเสียงที่ชัดเจนและแข็งกระด้างซึ่งได้รับอิทธิพลจากภาษาแต"
-                    + "้จิ๋ว"
-                    + " การออกเสียงพยัญชนะ สระ การผันวรรณยุกต์ที่ในภาษาไทยมาตรฐาน"
+                    + "้จิ๋ว การออกเสียงพยัญชนะ สระ การผันวรรณยุกต์ที่ในภาษาไทยมาตรฐาน"
                     + " มาจากสำเนียงถิ่นนี้ในขณะที่ภาษาไทยสำเนียงอื่นล้วนเหน่อทั้งสิ้น"
                     + " คำศัพท์ที่ใช้ในสำเนียงกรุงเทพจำนวนมากได้รับมาจากกลุ่มภาษาจีนเช่นคำว่า โป๊,"
                     + " เฮ็ง, อาหมวย, อาซิ่ม ซึ่งมาจากภาษาแต้จิ๋ว และจากภาษาจีนเช่น ถู(涂), ชิ่ว(去"
                     + " อ่านว่า\"ชู่\") และคำว่า ทาย(猜 อ่านว่า \"ชาย\") เป็นต้น"
                     + " เนื่องจากสำเนียงกรุงเทพได้รับอิทธิพลมาจากภาษาจีนดังนั้นตัวอักษร \"ร\""
-                    + " มักออกเสียงเหมารวมเป็น \"ล\" หรือคำควบกล่ำบางคำถูกละทิ้งไปด้วยเช่น รู้ เป็น"
-                    + " ลู้, เรื่อง เป็น เลื่อง หรือ ประเทศ เป็น ปะเทศ"
+                    + " มักออกเสียงเหมารวมเป็น \"ล\" หรือคำควบกล่ำบางคำถูกละทิ้งไปด้วยเช่น รู้"
+                    + " เป็น ลู้, เรื่อง เป็น เลื่อง หรือ ประเทศ เป็น ปะเทศ"
                     + " เป็นต้นสร้างความลำบากให้แก่ต่างชาติที่ต้องการเรียนภาษาไทย"
                     + " แต่อย่างไรก็ตามผู้ที่พูดสำเนียงถิ่นนี้ก็สามารถออกอักขระภาษาไทยตามมาตรฐานได"
                     + "้อย่างถูกต้องเพียงแต่มักเผลอไม่ค่อยออกเสียง"),
@@ -151,8 +152,7 @@
         }
     }
 
-    @Parameters(name = "mText={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {Text.ACCENT}, {Text.BIDI}, {Text.EMOJI}, {Text.EMPTY}, {Text.GERMAN},
@@ -161,15 +161,13 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public Text mText;
-
     @Test
-    public void timeBreakIterator() {
+    @Parameters(method = "getData")
+    public void timeBreakIterator(Text text) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            BreakIterator it = BreakIterator.getLineInstance(mText.mLocale);
-            it.setText(mText.mText);
+            BreakIterator it = BreakIterator.getLineInstance(text.mLocale);
+            it.setText(text.mText);
 
             while (it.next() != BreakIterator.DONE) {
                 // Keep iterating
@@ -178,12 +176,13 @@
     }
 
     @Test
-    public void timeIcuBreakIterator() {
+    @Parameters(method = "getData")
+    public void timeIcuBreakIterator(Text text) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             android.icu.text.BreakIterator it =
-                    android.icu.text.BreakIterator.getLineInstance(mText.mLocale);
-            it.setText(mText.mText);
+                    android.icu.text.BreakIterator.getLineInstance(text.mLocale);
+            it.setText(text.mText);
 
             while (it.next() != android.icu.text.BreakIterator.DONE) {
                 // Keep iterating
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java
index 855bb9a..b7b7e83 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java
@@ -20,11 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.io.File;
 import java.io.IOException;
@@ -34,13 +35,12 @@
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class BulkPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mAlign({0}), mSBuf({1}), mDBuf({2}), mSize({3})")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {true, MyBufferType.DIRECT, MyBufferType.DIRECT, 4096},
@@ -82,24 +82,12 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public boolean mAlign;
-
     enum MyBufferType {
         DIRECT,
         HEAP,
         MAPPED
     }
 
-    @Parameterized.Parameter(1)
-    public MyBufferType mSBuf;
-
-    @Parameterized.Parameter(2)
-    public MyBufferType mDBuf;
-
-    @Parameterized.Parameter(3)
-    public int mSize;
-
     public static ByteBuffer newBuffer(boolean aligned, MyBufferType bufferType, int bsize)
             throws IOException {
         int size = aligned ? bsize : bsize + 8 + 1;
@@ -126,13 +114,15 @@
     }
 
     @Test
-    public void timePut() throws Exception {
-        ByteBuffer src = BulkPerfTest.newBuffer(mAlign, mSBuf, mSize);
-        ByteBuffer data = BulkPerfTest.newBuffer(mAlign, mDBuf, mSize);
+    @Parameters(method = "getData")
+    public void timePut(boolean align, MyBufferType sBuf, MyBufferType dBuf, int size)
+            throws Exception {
+        ByteBuffer src = BulkPerfTest.newBuffer(align, sBuf, size);
+        ByteBuffer data = BulkPerfTest.newBuffer(align, dBuf, size);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAlign ? 0 : 1);
-            data.position(mAlign ? 0 : 1);
+            src.position(align ? 0 : 1);
+            data.position(align ? 0 : 1);
             src.put(data);
         }
     }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
index 4bd7c4e..9ac36d0 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
@@ -20,11 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.io.File;
 import java.io.IOException;
@@ -41,7 +42,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class ByteBufferPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -49,15 +50,14 @@
     public enum MyByteOrder {
         BIG(ByteOrder.BIG_ENDIAN),
         LITTLE(ByteOrder.LITTLE_ENDIAN);
-        final ByteOrder mByteOrder;
+        final ByteOrder byteOrder;
 
-        MyByteOrder(ByteOrder mByteOrder) {
-            this.mByteOrder = mByteOrder;
+        MyByteOrder(ByteOrder byteOrder) {
+            this.byteOrder = byteOrder;
         }
     }
 
-    @Parameters(name = "mByteOrder={0}, mAligned={1}, mBufferType={2}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {MyByteOrder.BIG, true, MyBufferType.DIRECT},
@@ -75,21 +75,12 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public MyByteOrder mByteOrder;
-
-    @Parameterized.Parameter(1)
-    public boolean mAligned;
-
     enum MyBufferType {
         DIRECT,
         HEAP,
         MAPPED;
     }
 
-    @Parameterized.Parameter(2)
-    public MyBufferType mBufferType;
-
     public static ByteBuffer newBuffer(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws IOException {
         int size = aligned ? 8192 : 8192 + 8 + 1;
@@ -115,7 +106,7 @@
                 result = fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size());
                 break;
         }
-        result.order(byteOrder.mByteOrder);
+        result.order(byteOrder.byteOrder);
         result.position(aligned ? 0 : 1);
         return result;
     }
@@ -125,11 +116,13 @@
     //
 
     @Test
-    public void timeByteBuffer_getByte() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_getByte(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.get();
             }
@@ -137,24 +130,28 @@
     }
 
     @Test
-    public void timeByteBuffer_getByteArray() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_getByteArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         byte[] dst = new byte[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
-                src.position(mAligned ? 0 : 1);
+                src.position(aligned ? 0 : 1);
                 src.get(dst);
             }
         }
     }
 
     @Test
-    public void timeByteBuffer_getByte_indexed() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_getByte_indexed(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.get(i);
             }
@@ -162,11 +159,13 @@
     }
 
     @Test
-    public void timeByteBuffer_getChar() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_getChar(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.getChar();
             }
@@ -174,9 +173,11 @@
     }
 
     @Test
-    public void timeCharBuffer_getCharArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeCharBuffer_getCharArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         CharBuffer src =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asCharBuffer();
+                ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asCharBuffer();
         char[] dst = new char[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -188,11 +189,13 @@
     }
 
     @Test
-    public void timeByteBuffer_getChar_indexed() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_getChar_indexed(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.getChar(i * 2);
             }
@@ -200,11 +203,13 @@
     }
 
     @Test
-    public void timeByteBuffer_getDouble() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_getDouble(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.getDouble();
             }
@@ -212,9 +217,11 @@
     }
 
     @Test
-    public void timeDoubleBuffer_getDoubleArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeDoubleBuffer_getDoubleArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         DoubleBuffer src =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asDoubleBuffer();
+                ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asDoubleBuffer();
         double[] dst = new double[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -226,11 +233,13 @@
     }
 
     @Test
-    public void timeByteBuffer_getFloat() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_getFloat(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.getFloat();
             }
@@ -238,9 +247,11 @@
     }
 
     @Test
-    public void timeFloatBuffer_getFloatArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeFloatBuffer_getFloatArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         FloatBuffer src =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asFloatBuffer();
+                ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asFloatBuffer();
         float[] dst = new float[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -252,11 +263,13 @@
     }
 
     @Test
-    public void timeByteBuffer_getInt() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_getInt(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.getInt();
             }
@@ -264,9 +277,10 @@
     }
 
     @Test
-    public void timeIntBuffer_getIntArray() throws Exception {
-        IntBuffer src =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asIntBuffer();
+    @Parameters(method = "getData")
+    public void timeIntBuffer_getIntArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        IntBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asIntBuffer();
         int[] dst = new int[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -278,11 +292,13 @@
     }
 
     @Test
-    public void timeByteBuffer_getLong() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_getLong(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.getLong();
             }
@@ -290,9 +306,11 @@
     }
 
     @Test
-    public void timeLongBuffer_getLongArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeLongBuffer_getLongArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         LongBuffer src =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asLongBuffer();
+                ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asLongBuffer();
         long[] dst = new long[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -304,11 +322,13 @@
     }
 
     @Test
-    public void timeByteBuffer_getShort() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_getShort(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.getShort();
             }
@@ -316,9 +336,11 @@
     }
 
     @Test
-    public void timeShortBuffer_getShortArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeShortBuffer_getShortArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ShortBuffer src =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asShortBuffer();
+                ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asShortBuffer();
         short[] dst = new short[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -334,8 +356,10 @@
     //
 
     @Test
-    public void timeByteBuffer_putByte() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_putByte(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             src.position(0);
@@ -346,24 +370,28 @@
     }
 
     @Test
-    public void timeByteBuffer_putByteArray() throws Exception {
-        ByteBuffer dst = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_putByteArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer dst = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         byte[] src = new byte[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
-                dst.position(mAligned ? 0 : 1);
+                dst.position(aligned ? 0 : 1);
                 dst.put(src);
             }
         }
     }
 
     @Test
-    public void timeByteBuffer_putChar() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_putChar(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.putChar(' ');
             }
@@ -371,9 +399,11 @@
     }
 
     @Test
-    public void timeCharBuffer_putCharArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeCharBuffer_putCharArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         CharBuffer dst =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asCharBuffer();
+                ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asCharBuffer();
         char[] src = new char[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -385,11 +415,13 @@
     }
 
     @Test
-    public void timeByteBuffer_putDouble() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_putDouble(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.putDouble(0.0);
             }
@@ -397,9 +429,11 @@
     }
 
     @Test
-    public void timeDoubleBuffer_putDoubleArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeDoubleBuffer_putDoubleArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         DoubleBuffer dst =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asDoubleBuffer();
+                ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asDoubleBuffer();
         double[] src = new double[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -411,11 +445,13 @@
     }
 
     @Test
-    public void timeByteBuffer_putFloat() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_putFloat(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.putFloat(0.0f);
             }
@@ -423,9 +459,11 @@
     }
 
     @Test
-    public void timeFloatBuffer_putFloatArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeFloatBuffer_putFloatArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         FloatBuffer dst =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asFloatBuffer();
+                ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asFloatBuffer();
         float[] src = new float[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -437,11 +475,13 @@
     }
 
     @Test
-    public void timeByteBuffer_putInt() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_putInt(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.putInt(0);
             }
@@ -449,9 +489,10 @@
     }
 
     @Test
-    public void timeIntBuffer_putIntArray() throws Exception {
-        IntBuffer dst =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asIntBuffer();
+    @Parameters(method = "getData")
+    public void timeIntBuffer_putIntArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        IntBuffer dst = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asIntBuffer();
         int[] src = new int[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -463,11 +504,13 @@
     }
 
     @Test
-    public void timeByteBuffer_putLong() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_putLong(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.putLong(0L);
             }
@@ -475,9 +518,11 @@
     }
 
     @Test
-    public void timeLongBuffer_putLongArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeLongBuffer_putLongArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         LongBuffer dst =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asLongBuffer();
+                ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asLongBuffer();
         long[] src = new long[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -489,11 +534,13 @@
     }
 
     @Test
-    public void timeByteBuffer_putShort() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeByteBuffer_putShort(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
                 src.putShort((short) 0);
             }
@@ -501,9 +548,11 @@
     }
 
     @Test
-    public void timeShortBuffer_putShortArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeShortBuffer_putShortArray(
+            MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ShortBuffer dst =
-                ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asShortBuffer();
+                ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asShortBuffer();
         short[] src = new short[1024];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -515,6 +564,7 @@
     }
 
     @Test
+    @Parameters(method = "getData")
     public void time_new_byteArray() throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -523,6 +573,7 @@
     }
 
     @Test
+    @Parameters(method = "getData")
     public void time_ByteBuffer_allocate() throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java
index 81f9e59..5dd9d6e 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java
@@ -20,23 +20,23 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class ByteBufferScalarVersusVectorPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mByteOrder={0}, mAligned={1}, mBufferType={2}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {
@@ -102,19 +102,15 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public ByteBufferPerfTest.MyByteOrder mByteOrder;
-
-    @Parameterized.Parameter(1)
-    public boolean mAligned;
-
-    @Parameterized.Parameter(2)
-    public ByteBufferPerfTest.MyBufferType mBufferType;
-
     @Test
-    public void timeManualByteBufferCopy() throws Exception {
-        ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
-        ByteBuffer dst = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType);
+    @Parameters(method = "getData")
+    public void timeManualByteBufferCopy(
+            ByteBufferPerfTest.MyByteOrder byteOrder,
+            boolean aligned,
+            ByteBufferPerfTest.MyBufferType bufferType)
+            throws Exception {
+        ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
+        ByteBuffer dst = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             src.position(0);
@@ -126,23 +122,25 @@
     }
 
     @Test
-    public void timeByteBufferBulkGet() throws Exception {
-        ByteBuffer src = ByteBuffer.allocate(mAligned ? 8192 : 8192 + 1);
+    @Parameters({"true", "false"})
+    public void timeByteBufferBulkGet(boolean aligned) throws Exception {
+        ByteBuffer src = ByteBuffer.allocate(aligned ? 8192 : 8192 + 1);
         byte[] dst = new byte[8192];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             src.get(dst, 0, dst.length);
         }
     }
 
     @Test
-    public void timeDirectByteBufferBulkGet() throws Exception {
-        ByteBuffer src = ByteBuffer.allocateDirect(mAligned ? 8192 : 8192 + 1);
+    @Parameters({"true", "false"})
+    public void timeDirectByteBufferBulkGet(boolean aligned) throws Exception {
+        ByteBuffer src = ByteBuffer.allocateDirect(aligned ? 8192 : 8192 + 1);
         byte[] dst = new byte[8192];
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            src.position(mAligned ? 0 : 1);
+            src.position(aligned ? 0 : 1);
             src.get(dst, 0, dst.length);
         }
     }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java
index 28ec6de..0a59899 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java
@@ -20,12 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -34,13 +34,12 @@
  * Tests various Character methods, intended for testing multiple implementations against each
  * other.
  */
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CharacterPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mCharacterSet({0}), mOverload({1})")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {CharacterSet.ASCII, Overload.CHAR},
@@ -50,17 +49,10 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public CharacterSet mCharacterSet;
-
-    @Parameterized.Parameter(1)
-    public Overload mOverload;
-
     private char[] mChars;
 
-    @Before
-    public void setUp() throws Exception {
-        this.mChars = mCharacterSet.mChars;
+    public void setUp(CharacterSet characterSet) {
+        this.mChars = characterSet.mChars;
     }
 
     public enum Overload {
@@ -87,10 +79,12 @@
 
     // A fake benchmark to give us a baseline.
     @Test
-    public void timeIsSpace() {
+    @Parameters(method = "getData")
+    public void timeIsSpace(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         boolean fake = false;
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     fake ^= ((char) ch == ' ');
@@ -106,9 +100,11 @@
     }
 
     @Test
-    public void timeDigit() {
+    @Parameters(method = "getData")
+    public void timeDigit(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.digit(mChars[ch], 10);
@@ -124,9 +120,11 @@
     }
 
     @Test
-    public void timeGetNumericValue() {
+    @Parameters(method = "getData")
+    public void timeGetNumericValue(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.getNumericValue(mChars[ch]);
@@ -142,9 +140,11 @@
     }
 
     @Test
-    public void timeIsDigit() {
+    @Parameters(method = "getData")
+    public void timeIsDigit(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.isDigit(mChars[ch]);
@@ -160,9 +160,11 @@
     }
 
     @Test
-    public void timeIsIdentifierIgnorable() {
+    @Parameters(method = "getData")
+    public void timeIsIdentifierIgnorable(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.isIdentifierIgnorable(mChars[ch]);
@@ -178,9 +180,11 @@
     }
 
     @Test
-    public void timeIsJavaIdentifierPart() {
+    @Parameters(method = "getData")
+    public void timeIsJavaIdentifierPart(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.isJavaIdentifierPart(mChars[ch]);
@@ -196,9 +200,11 @@
     }
 
     @Test
-    public void timeIsJavaIdentifierStart() {
+    @Parameters(method = "getData")
+    public void timeIsJavaIdentifierStart(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.isJavaIdentifierStart(mChars[ch]);
@@ -214,9 +220,11 @@
     }
 
     @Test
-    public void timeIsLetter() {
+    @Parameters(method = "getData")
+    public void timeIsLetter(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.isLetter(mChars[ch]);
@@ -232,9 +240,11 @@
     }
 
     @Test
-    public void timeIsLetterOrDigit() {
+    @Parameters(method = "getData")
+    public void timeIsLetterOrDigit(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.isLetterOrDigit(mChars[ch]);
@@ -250,9 +260,11 @@
     }
 
     @Test
-    public void timeIsLowerCase() {
+    @Parameters(method = "getData")
+    public void timeIsLowerCase(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.isLowerCase(mChars[ch]);
@@ -268,9 +280,11 @@
     }
 
     @Test
-    public void timeIsSpaceChar() {
+    @Parameters(method = "getData")
+    public void timeIsSpaceChar(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.isSpaceChar(mChars[ch]);
@@ -286,9 +300,11 @@
     }
 
     @Test
-    public void timeIsUpperCase() {
+    @Parameters(method = "getData")
+    public void timeIsUpperCase(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.isUpperCase(mChars[ch]);
@@ -304,9 +320,11 @@
     }
 
     @Test
-    public void timeIsWhitespace() {
+    @Parameters(method = "getData")
+    public void timeIsWhitespace(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.isWhitespace(mChars[ch]);
@@ -322,9 +340,11 @@
     }
 
     @Test
-    public void timeToLowerCase() {
+    @Parameters(method = "getData")
+    public void timeToLowerCase(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.toLowerCase(mChars[ch]);
@@ -340,9 +360,11 @@
     }
 
     @Test
-    public void timeToUpperCase() {
+    @Parameters(method = "getData")
+    public void timeToUpperCase(CharacterSet characterSet, Overload overload) {
+        setUp(characterSet);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        if (mOverload == Overload.CHAR) {
+        if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
                     Character.toUpperCase(mChars[ch]);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java
index 603b182..8da13a9 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java
@@ -20,44 +20,40 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
 import java.nio.charset.Charset;
-import java.util.Arrays;
-import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CharsetForNamePerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameterized.Parameters(name = "mCharsetName({0})")
-    public static Collection<Object[]> data() {
-        return Arrays.asList(
-                new Object[][] {
-                    {"UTF-16"},
-                    {"UTF-8"},
-                    {"UTF8"},
-                    {"ISO-8859-1"},
-                    {"8859_1"},
-                    {"ISO-8859-2"},
-                    {"8859_2"},
-                    {"US-ASCII"},
-                    {"ASCII"},
-                });
+    public static String[] charsetNames() {
+        return new String[] {
+            "UTF-16",
+            "UTF-8",
+            "UTF8",
+            "ISO-8859-1",
+            "8859_1",
+            "ISO-8859-2",
+            "8859_2",
+            "US-ASCII",
+            "ASCII",
+        };
     }
 
-    @Parameterized.Parameter(0)
-    public String mCharsetName;
-
     @Test
-    public void timeCharsetForName() throws Exception {
+    @Parameters(method = "charsetNames")
+    public void timeCharsetForName(String charsetName) throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            Charset.forName(mCharsetName);
+            Charset.forName(charsetName);
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java
index 437d186..048c50f 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java
@@ -20,22 +20,22 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CharsetPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mLength({0}), mName({1})")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {1, "UTF-16"},
@@ -86,24 +86,20 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public int mLength;
-
-    @Parameterized.Parameter(1)
-    public String mName;
-
     @Test
-    public void time_new_String_BString() throws Exception {
-        byte[] bytes = makeBytes(makeString(mLength));
+    @Parameters(method = "getData")
+    public void time_new_String_BString(int length, String name) throws Exception {
+        byte[] bytes = makeBytes(makeString(length));
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            new String(bytes, mName);
+            new String(bytes, name);
         }
     }
 
     @Test
-    public void time_new_String_BII() throws Exception {
-        byte[] bytes = makeBytes(makeString(mLength));
+    @Parameters(method = "getData")
+    public void time_new_String_BII(int length, String name) throws Exception {
+        byte[] bytes = makeBytes(makeString(length));
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             new String(bytes, 0, bytes.length);
@@ -111,20 +107,22 @@
     }
 
     @Test
-    public void time_new_String_BIIString() throws Exception {
-        byte[] bytes = makeBytes(makeString(mLength));
+    @Parameters(method = "getData")
+    public void time_new_String_BIIString(int length, String name) throws Exception {
+        byte[] bytes = makeBytes(makeString(length));
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            new String(bytes, 0, bytes.length, mName);
+            new String(bytes, 0, bytes.length, name);
         }
     }
 
     @Test
-    public void time_String_getBytes() throws Exception {
-        String string = makeString(mLength);
+    @Parameters(method = "getData")
+    public void time_String_getBytes(int length, String name) throws Exception {
+        String string = makeString(length);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            string.getBytes(mName);
+            string.getBytes(name);
         }
     }
 
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
index 15c27f2..42b0588 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
@@ -20,11 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
 import java.security.spec.AlgorithmParameterSpec;
 import java.util.ArrayList;
@@ -42,17 +43,13 @@
  * Cipher benchmarks. Only runs on AES currently because of the combinatorial explosion of the test
  * as it stands.
  */
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CipherPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameterized.Parameters(
-            name =
-                    "mMode({0}), mPadding({1}), mKeySize({2}), mInputSize({3}),"
-                            + " mImplementation({4})")
-    public static Collection cases() {
-        int[] mKeySizes = new int[] {128, 192, 256};
+    public static Collection getCases() {
+        int[] keySizes = new int[] {128, 192, 256};
         int[] inputSizes = new int[] {16, 32, 64, 128, 1024, 8192};
         final List<Object[]> params = new ArrayList<>();
         for (Mode mode : Mode.values()) {
@@ -71,11 +68,11 @@
                             && implementation == Implementation.OpenSSL) {
                         continue;
                     }
-                    for (int mKeySize : mKeySizes) {
+                    for (int keySize : keySizes) {
                         for (int inputSize : inputSizes) {
                             params.add(
                                     new Object[] {
-                                        mode, padding, mKeySize, inputSize, implementation
+                                        mode, padding, keySize, inputSize, implementation
                                     });
                         }
                     }
@@ -107,9 +104,6 @@
         AES,
     };
 
-    @Parameterized.Parameter(0)
-    public Mode mMode;
-
     public enum Mode {
         CBC,
         CFB,
@@ -118,23 +112,11 @@
         OFB,
     };
 
-    @Parameterized.Parameter(1)
-    public Padding mPadding;
-
     public enum Padding {
         NOPADDING,
         PKCS1PADDING,
     };
 
-    @Parameterized.Parameter(2)
-    public int mKeySize;
-
-    @Parameterized.Parameter(3)
-    public int mInputSize;
-
-    @Parameterized.Parameter(4)
-    public Implementation mImplementation;
-
     public enum Implementation {
         OpenSSL,
         BouncyCastle
@@ -156,21 +138,20 @@
 
     private AlgorithmParameterSpec mSpec;
 
-    @Before
-    public void setUp() throws Exception {
-        mCipherAlgorithm =
-                mAlgorithm.toString() + "/" + mMode.toString() + "/" + mPadding.toString();
+    public void setUp(Mode mode, Padding padding, int keySize, Implementation implementation)
+            throws Exception {
+        mCipherAlgorithm = mAlgorithm.toString() + "/" + mode.toString() + "/" + padding.toString();
 
         String mKeyAlgorithm = mAlgorithm.toString();
-        mKey = sKeySizes.get(mKeySize);
+        mKey = sKeySizes.get(keySize);
         if (mKey == null) {
             KeyGenerator generator = KeyGenerator.getInstance(mKeyAlgorithm);
-            generator.init(mKeySize);
+            generator.init(keySize);
             mKey = generator.generateKey();
-            sKeySizes.put(mKeySize, mKey);
+            sKeySizes.put(keySize, mKey);
         }
 
-        switch (mImplementation) {
+        switch (implementation) {
             case OpenSSL:
                 mProviderName = "AndroidOpenSSL";
                 break;
@@ -178,10 +159,10 @@
                 mProviderName = "BC";
                 break;
             default:
-                throw new RuntimeException(mImplementation.toString());
+                throw new RuntimeException(implementation.toString());
         }
 
-        if (mMode != Mode.ECB) {
+        if (mode != Mode.ECB) {
             mSpec = new IvParameterSpec(IV);
         }
 
@@ -193,18 +174,26 @@
     }
 
     @Test
-    public void timeEncrypt() throws Exception {
+    @Parameters(method = "getCases")
+    public void timeEncrypt(
+            Mode mode, Padding padding, int keySize, int inputSize, Implementation implementation)
+            throws Exception {
+        setUp(mode, padding, keySize, implementation);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mCipherEncrypt.doFinal(DATA, 0, mInputSize, mOutput);
+            mCipherEncrypt.doFinal(DATA, 0, inputSize, mOutput);
         }
     }
 
     @Test
-    public void timeDecrypt() throws Exception {
+    @Parameters(method = "getCases")
+    public void timeDecrypt(
+            Mode mode, Padding padding, int keySize, int inputSize, Implementation implementation)
+            throws Exception {
+        setUp(mode, padding, keySize, implementation);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mCipherDecrypt.doFinal(DATA, 0, mInputSize, mOutput);
+            mCipherDecrypt.doFinal(DATA, 0, inputSize, mOutput);
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java
index a89efff..69197c3 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java
@@ -20,11 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -35,19 +36,15 @@
 import java.util.Random;
 import java.util.Vector;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CollectionsPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mArrayListLength({0})")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{4}, {16}, {64}, {256}, {1024}});
     }
 
-    @Parameterized.Parameter(0)
-    public int arrayListLength;
-
     public static Comparator<Integer> REVERSE =
             new Comparator<Integer>() {
                 @Override
@@ -59,7 +56,8 @@
             };
 
     @Test
-    public void timeSort_arrayList() throws Exception {
+    @Parameters(method = "getData")
+    public void timeSort_arrayList(int arrayListLength) throws Exception {
         List<Integer> input = buildList(arrayListLength, ArrayList.class);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -68,7 +66,8 @@
     }
 
     @Test
-    public void timeSortWithComparator_arrayList() throws Exception {
+    @Parameters(method = "getData")
+    public void timeSortWithComparator_arrayList(int arrayListLength) throws Exception {
         List<Integer> input = buildList(arrayListLength, ArrayList.class);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -77,7 +76,8 @@
     }
 
     @Test
-    public void timeSort_vector() throws Exception {
+    @Parameters(method = "getData")
+    public void timeSort_vector(int arrayListLength) throws Exception {
         List<Integer> input = buildList(arrayListLength, Vector.class);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -86,7 +86,8 @@
     }
 
     @Test
-    public void timeSortWithComparator_vector() throws Exception {
+    @Parameters(method = "getData")
+    public void timeSortWithComparator_vector(int arrayListLength) throws Exception {
         List<Integer> input = buildList(arrayListLength, Vector.class);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java
index 4ff3ba5..8391203 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java
@@ -20,11 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
 import java.net.URI;
 import java.net.URL;
@@ -32,39 +33,39 @@
 import java.util.Collection;
 import java.util.List;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class EqualsHashCodePerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
     private enum Type {
         URI() {
-            @Override Object newInstance(String text) throws Exception {
+            @Override
+            Object newInstance(String text) throws Exception {
                 return new URI(text);
             }
         },
         URL() {
-            @Override Object newInstance(String text) throws Exception {
+            @Override
+            Object newInstance(String text) throws Exception {
                 return new URL(text);
             }
         };
+
         abstract Object newInstance(String text) throws Exception;
     }
 
-    private static final String QUERY = "%E0%AE%A8%E0%AE%BE%E0%AE%AE%E0%AF%8D+%E0%AE%AE%E0%AF%81%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AE%BF%E0%AE%AF%E0%AE%AE%E0%AE%BE%E0%AE%A9%2C+%E0%AE%9A%E0%AF%81%E0%AE%B5%E0%AE%BE%E0%AE%B0%E0%AE%B8%E0%AF%8D%E0%AE%AF%E0%AE%AE%E0%AE%BE%E0%AE%A9+%E0%AE%87%E0%AE%B0%E0%AF%81%E0%AE%AA%E0%AF%8D%E0%AE%AA%E0%AF%87%E0%AE%BE%E0%AE%AE%E0%AF%8D%2C+%E0%AE%86%E0%AE%A9%E0%AE%BE%E0%AE%B2%E0%AF%8D+%E0%AE%9A%E0%AE%BF%E0%AE%B2+%E0%AE%A8%E0%AF%87%E0%AE%B0%E0%AE%99%E0%AF%8D%E0%AE%95%E0%AE%B3%E0%AE%BF%E0%AE%B2%E0%AF%8D+%E0%AE%9A%E0%AF%82%E0%AE%B4%E0%AF%8D%E0%AE%A8%E0%AE%BF%E0%AE%B2%E0%AF%88+%E0%AE%8F%E0%AE%B1%E0%AF%8D%E0%AE%AA%E0%AE%9F%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%8E%E0%AE%A9%E0%AF%8D%E0%AE%AA%E0%AE%A4%E0%AE%BE%E0%AE%B2%E0%AF%8D+%E0%AE%AA%E0%AE%A3%E0%AE%BF%E0%AE%AF%E0%AF%88%E0%AE%AF%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%B5%E0%AE%B2%E0%AE%BF+%E0%AE%85%E0%AE%B5%E0%AE%B0%E0%AF%88+%E0%AE%9A%E0%AE%BF%E0%AE%B2+%E0%AE%AA%E0%AF%86%E0%AE%B0%E0%AE%BF%E0%AE%AF+%E0%AE%95%E0%AF%86%E0%AE%BE%E0%AE%B3%E0%AF%8D%E0%AE%AE%E0%AF%81%E0%AE%A4%E0%AE%B2%E0%AF%8D+%E0%AE%AE%E0%AF%81%E0%AE%9F%E0%AE%BF%E0%AE%AF%E0%AF%81%E0%AE%AE%E0%AF%8D.+%E0%AE%85%E0%AE%A4%E0%AF%81+%E0%AE%9A%E0%AE%BF%E0%AE%B2+%E0%AE%A8%E0%AE%A9%E0%AF%8D%E0%AE%AE%E0%AF%88%E0%AE%95%E0%AE%B3%E0%AF%88+%E0%AE%AA%E0%AF%86%E0%AE%B1+%E0%AE%A4%E0%AE%B5%E0%AE%BF%E0%AE%B0%2C+%E0%AE%8E%E0%AE%AA%E0%AF%8D%E0%AE%AA%E0%AF%87%E0%AE%BE%E0%AE%A4%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%89%E0%AE%B4%E0%AF%88%E0%AE%95%E0%AF%8D%E0%AE%95+%E0%AE%89%E0%AE%9F%E0%AE%B1%E0%AF%8D%E0%AE%AA%E0%AE%AF%E0%AE%BF%E0%AE%B1%E0%AF%8D%E0%AE%9A%E0%AE%BF+%E0%AE%AE%E0%AF%87%E0%AE%B1%E0%AF%8D%E0%AE%95%E0%AF%86%E0%AE%BE%E0%AE%B3%E0%AF%8D%E0%AE%95%E0%AE%BF%E0%AE%B1%E0%AE%A4%E0%AF%81+%E0%AE%8E%E0%AE%99%E0%AF%8D%E0%AE%95%E0%AE%B3%E0%AF%81%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AF%81+%E0%AE%87%E0%AE%A4%E0%AF%81+%E0%AE%92%E0%AE%B0%E0%AF%81+%E0%AE%9A%E0%AE%BF%E0%AE%B1%E0%AE%BF%E0%AE%AF+%E0%AE%89%E0%AE%A4%E0%AE%BE%E0%AE%B0%E0%AE%A3%E0%AE%AE%E0%AF%8D%2C+%E0%AE%8E%E0%AE%9F%E0%AF%81%E0%AE%95%E0%AF%8D%E0%AE%95.+%E0%AE%B0%E0%AE%AF%E0%AE%BF%E0%AE%B2%E0%AF%8D+%E0%AE%8E%E0%AE%A8%E0%AF%8D%E0%AE%A4+%E0%AE%B5%E0%AE%BF%E0%AE%B3%E0%AF%88%E0%AE%B5%E0%AE%BE%E0%AE%95+%E0%AE%87%E0%AE%A9%E0%AF%8D%E0%AE%AA%E0%AE%AE%E0%AF%8D+%E0%AE%86%E0%AE%A9%E0%AF%8D%E0%AE%B2%E0%AF%88%E0%AE%A9%E0%AF%8D+%E0%AE%AA%E0%AE%AF%E0%AE%A9%E0%AF%8D%E0%AE%AA%E0%AE%BE%E0%AE%9F%E0%AF%81%E0%AE%95%E0%AE%B3%E0%AF%8D+%E0%AE%87%E0%AE%B0%E0%AF%81%E0%AE%95%E0%AF%8D%E0%AE%95+%E0%AE%B5%E0%AF%87%E0%AE%A3%E0%AF%8D%E0%AE%9F%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%A4%E0%AE%AF%E0%AE%BE%E0%AE%B0%E0%AE%BF%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%A4%E0%AE%B5%E0%AE%B1%E0%AF%81+%E0%AE%95%E0%AE%A3%E0%AF%8D%E0%AE%9F%E0%AF%81%E0%AE%AA%E0%AE%BF%E0%AE%9F%E0%AE%BF%E0%AE%95%E0%AF%8D%E0%AE%95+%E0%AE%B5%E0%AE%B0%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%A8%E0%AE%BE%E0%AE%AE%E0%AF%8D+%E0%AE%A4%E0%AE%B1%E0%AF%8D%E0%AE%AA%E0%AF%87%E0%AE%BE%E0%AE%A4%E0%AF%81+%E0%AE%87%E0%AE%B0%E0%AF%81%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AE%BF%E0%AE%B1%E0%AF%87%E0%AE%BE%E0%AE%AE%E0%AF%8D.+%E0%AE%87%E0%AE%A8%E0%AF%8D%E0%AE%A4+%E0%AE%A8%E0%AE%BF%E0%AE%95%E0%AE%B4%E0%AF%8D%E0%AE%B5%E0%AF%81%E0%AE%95%E0%AE%B3%E0%AE%BF%E0%AE%B2%E0%AF%8D+%E0%AE%9A%E0%AF%86%E0%AE%AF%E0%AF%8D%E0%AE%A4%E0%AE%AA%E0%AE%BF%E0%AE%A9%E0%AF%8D+%E0%AE%85%E0%AE%AE%E0%AF%88%E0%AE%AA%E0%AF%8D%E0%AE%AA%E0%AE%BF%E0%AE%A9%E0%AF%8D+%E0%AE%95%E0%AE%A3%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AF%81%2C+%E0%AE%85%E0%AE%B5%E0%AE%B0%E0%AF%8D%E0%AE%95%E0%AE%B3%E0%AF%8D+%E0%AE%A4%E0%AE%B5%E0%AE%B1%E0%AF%81+%E0%AE%B5%E0%AE%BF%E0%AE%9F%E0%AF%8D%E0%AE%9F%E0%AF%81+quae+%E0%AE%AA%E0%AE%9F%E0%AF%8D%E0%AE%9F%E0%AE%B1%E0%AF%88+%E0%AE%A8%E0%AF%80%E0%AE%99%E0%AF%8D%E0%AE%95%E0%AE%B3%E0%AF%8D+%E0%AE%AA%E0%AE%B0%E0%AE%BF%E0%AE%A8%E0%AF%8D%E0%AE%A4%E0%AF%81%E0%AE%B0%E0%AF%88%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AE%BF%E0%AE%B1%E0%AF%87%E0%AE%BE%E0%AE%AE%E0%AF%8D+%E0%AE%AE%E0%AF%86%E0%AE%A9%E0%AF%8D%E0%AE%AE%E0%AF%88%E0%AE%AF%E0%AE%BE%E0%AE%95+%E0%AE%AE%E0%AE%BE%E0%AE%B1%E0%AF%81%E0%AE%AE%E0%AF%8D";
+    private static final String QUERY =
+            "%E0%AE%A8%E0%AE%BE%E0%AE%AE%E0%AF%8D+%E0%AE%AE%E0%AF%81%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AE%BF%E0%AE%AF%E0%AE%AE%E0%AE%BE%E0%AE%A9%2C+%E0%AE%9A%E0%AF%81%E0%AE%B5%E0%AE%BE%E0%AE%B0%E0%AE%B8%E0%AF%8D%E0%AE%AF%E0%AE%AE%E0%AE%BE%E0%AE%A9+%E0%AE%87%E0%AE%B0%E0%AF%81%E0%AE%AA%E0%AF%8D%E0%AE%AA%E0%AF%87%E0%AE%BE%E0%AE%AE%E0%AF%8D%2C+%E0%AE%86%E0%AE%A9%E0%AE%BE%E0%AE%B2%E0%AF%8D+%E0%AE%9A%E0%AE%BF%E0%AE%B2+%E0%AE%A8%E0%AF%87%E0%AE%B0%E0%AE%99%E0%AF%8D%E0%AE%95%E0%AE%B3%E0%AE%BF%E0%AE%B2%E0%AF%8D+%E0%AE%9A%E0%AF%82%E0%AE%B4%E0%AF%8D%E0%AE%A8%E0%AE%BF%E0%AE%B2%E0%AF%88+%E0%AE%8F%E0%AE%B1%E0%AF%8D%E0%AE%AA%E0%AE%9F%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%8E%E0%AE%A9%E0%AF%8D%E0%AE%AA%E0%AE%A4%E0%AE%BE%E0%AE%B2%E0%AF%8D+%E0%AE%AA%E0%AE%A3%E0%AE%BF%E0%AE%AF%E0%AF%88%E0%AE%AF%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%B5%E0%AE%B2%E0%AE%BF+%E0%AE%85%E0%AE%B5%E0%AE%B0%E0%AF%88+%E0%AE%9A%E0%AE%BF%E0%AE%B2+%E0%AE%AA%E0%AF%86%E0%AE%B0%E0%AE%BF%E0%AE%AF+%E0%AE%95%E0%AF%86%E0%AE%BE%E0%AE%B3%E0%AF%8D%E0%AE%AE%E0%AF%81%E0%AE%A4%E0%AE%B2%E0%AF%8D+%E0%AE%AE%E0%AF%81%E0%AE%9F%E0%AE%BF%E0%AE%AF%E0%AF%81%E0%AE%AE%E0%AF%8D.+%E0%AE%85%E0%AE%A4%E0%AF%81+%E0%AE%9A%E0%AE%BF%E0%AE%B2+%E0%AE%A8%E0%AE%A9%E0%AF%8D%E0%AE%AE%E0%AF%88%E0%AE%95%E0%AE%B3%E0%AF%88+%E0%AE%AA%E0%AF%86%E0%AE%B1+%E0%AE%A4%E0%AE%B5%E0%AE%BF%E0%AE%B0%2C+%E0%AE%8E%E0%AE%AA%E0%AF%8D%E0%AE%AA%E0%AF%87%E0%AE%BE%E0%AE%A4%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%89%E0%AE%B4%E0%AF%88%E0%AE%95%E0%AF%8D%E0%AE%95+%E0%AE%89%E0%AE%9F%E0%AE%B1%E0%AF%8D%E0%AE%AA%E0%AE%AF%E0%AE%BF%E0%AE%B1%E0%AF%8D%E0%AE%9A%E0%AE%BF+%E0%AE%AE%E0%AF%87%E0%AE%B1%E0%AF%8D%E0%AE%95%E0%AF%86%E0%AE%BE%E0%AE%B3%E0%AF%8D%E0%AE%95%E0%AE%BF%E0%AE%B1%E0%AE%A4%E0%AF%81+%E0%AE%8E%E0%AE%99%E0%AF%8D%E0%AE%95%E0%AE%B3%E0%AF%81%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AF%81+%E0%AE%87%E0%AE%A4%E0%AF%81+%E0%AE%92%E0%AE%B0%E0%AF%81+%E0%AE%9A%E0%AE%BF%E0%AE%B1%E0%AE%BF%E0%AE%AF+%E0%AE%89%E0%AE%A4%E0%AE%BE%E0%AE%B0%E0%AE%A3%E0%AE%AE%E0%AF%8D%2C+%E0%AE%8E%E0%AE%9F%E0%AF%81%E0%AE%95%E0%AF%8D%E0%AE%95.+%E0%AE%B0%E0%AE%AF%E0%AE%BF%E0%AE%B2%E0%AF%8D+%E0%AE%8E%E0%AE%A8%E0%AF%8D%E0%AE%A4+%E0%AE%B5%E0%AE%BF%E0%AE%B3%E0%AF%88%E0%AE%B5%E0%AE%BE%E0%AE%95+%E0%AE%87%E0%AE%A9%E0%AF%8D%E0%AE%AA%E0%AE%AE%E0%AF%8D+%E0%AE%86%E0%AE%A9%E0%AF%8D%E0%AE%B2%E0%AF%88%E0%AE%A9%E0%AF%8D+%E0%AE%AA%E0%AE%AF%E0%AE%A9%E0%AF%8D%E0%AE%AA%E0%AE%BE%E0%AE%9F%E0%AF%81%E0%AE%95%E0%AE%B3%E0%AF%8D+%E0%AE%87%E0%AE%B0%E0%AF%81%E0%AE%95%E0%AF%8D%E0%AE%95+%E0%AE%B5%E0%AF%87%E0%AE%A3%E0%AF%8D%E0%AE%9F%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%A4%E0%AE%AF%E0%AE%BE%E0%AE%B0%E0%AE%BF%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%A4%E0%AE%B5%E0%AE%B1%E0%AF%81+%E0%AE%95%E0%AE%A3%E0%AF%8D%E0%AE%9F%E0%AF%81%E0%AE%AA%E0%AE%BF%E0%AE%9F%E0%AE%BF%E0%AE%95%E0%AF%8D%E0%AE%95+%E0%AE%B5%E0%AE%B0%E0%AF%81%E0%AE%AE%E0%AF%8D+%E0%AE%A8%E0%AE%BE%E0%AE%AE%E0%AF%8D+%E0%AE%A4%E0%AE%B1%E0%AF%8D%E0%AE%AA%E0%AF%87%E0%AE%BE%E0%AE%A4%E0%AF%81+%E0%AE%87%E0%AE%B0%E0%AF%81%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AE%BF%E0%AE%B1%E0%AF%87%E0%AE%BE%E0%AE%AE%E0%AF%8D.+%E0%AE%87%E0%AE%A8%E0%AF%8D%E0%AE%A4+%E0%AE%A8%E0%AE%BF%E0%AE%95%E0%AE%B4%E0%AF%8D%E0%AE%B5%E0%AF%81%E0%AE%95%E0%AE%B3%E0%AE%BF%E0%AE%B2%E0%AF%8D+%E0%AE%9A%E0%AF%86%E0%AE%AF%E0%AF%8D%E0%AE%A4%E0%AE%AA%E0%AE%BF%E0%AE%A9%E0%AF%8D+%E0%AE%85%E0%AE%AE%E0%AF%88%E0%AE%AA%E0%AF%8D%E0%AE%AA%E0%AE%BF%E0%AE%A9%E0%AF%8D+%E0%AE%95%E0%AE%A3%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AF%81%2C+%E0%AE%85%E0%AE%B5%E0%AE%B0%E0%AF%8D%E0%AE%95%E0%AE%B3%E0%AF%8D+%E0%AE%A4%E0%AE%B5%E0%AE%B1%E0%AF%81+%E0%AE%B5%E0%AE%BF%E0%AE%9F%E0%AF%8D%E0%AE%9F%E0%AF%81+quae+%E0%AE%AA%E0%AE%9F%E0%AF%8D%E0%AE%9F%E0%AE%B1%E0%AF%88+%E0%AE%A8%E0%AF%80%E0%AE%99%E0%AF%8D%E0%AE%95%E0%AE%B3%E0%AF%8D+%E0%AE%AA%E0%AE%B0%E0%AE%BF%E0%AE%A8%E0%AF%8D%E0%AE%A4%E0%AF%81%E0%AE%B0%E0%AF%88%E0%AE%95%E0%AF%8D%E0%AE%95%E0%AE%BF%E0%AE%B1%E0%AF%87%E0%AE%BE%E0%AE%AE%E0%AF%8D+%E0%AE%AE%E0%AF%86%E0%AE%A9%E0%AF%8D%E0%AE%AE%E0%AF%88%E0%AE%AF%E0%AE%BE%E0%AE%95+%E0%AE%AE%E0%AE%BE%E0%AE%B1%E0%AF%81%E0%AE%AE%E0%AF%8D";
 
-    @Parameterized.Parameters(name = "mType({0})")
-    public static Collection cases() {
+    public static Collection getCases() {
         final List<Object[]> params = new ArrayList<>();
         for (Type type : Type.values()) {
-            params.add(new Object[]{type});
+            params.add(new Object[] {type});
         }
         return params;
     }
 
-    @Parameterized.Parameter(0)
-    public Type mType;
-
     Object mA1;
     Object mA2;
     Object mB1;
@@ -73,20 +74,13 @@
     Object mC1;
     Object mC2;
 
-    @Before
-    public void setUp() throws Exception {
-        mA1 = mType.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
-        mA2 = mType.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
-        mB1 = mType.newInstance("http://developer.android.com/reference/java/net/URI.html");
-        mB2 = mType.newInstance("http://developer.android.com/reference/java/net/URI.html");
-
-        mC1 = mType.newInstance("http://developer.android.com/query?q=" + QUERY);
-        // Replace the very last char.
-        mC2 = mType.newInstance("http://developer.android.com/query?q=" + QUERY.substring(0, QUERY.length() - 3) + "%AF");
-    }
-
     @Test
-    public void timeEquals() {
+    @Parameters(method = "getCases")
+    public void timeEquals(Type type) throws Exception {
+        mA1 = type.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
+        mA2 = type.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
+        mB1 = type.newInstance("http://developer.android.com/reference/java/net/URI.html");
+        mB2 = type.newInstance("http://developer.android.com/reference/java/net/URI.html");
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             mA1.equals(mB1);
@@ -96,7 +90,10 @@
     }
 
     @Test
-    public void timeHashCode() {
+    @Parameters(method = "getCases")
+    public void timeHashCode(Type type) throws Exception {
+        mA1 = type.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
+        mB1 = type.newInstance("http://developer.android.com/reference/java/net/URI.html");
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             mA1.hashCode();
@@ -105,7 +102,15 @@
     }
 
     @Test
-    public void timeEqualsWithHeavilyEscapedComponent() {
+    @Parameters(method = "getCases")
+    public void timeEqualsWithHeavilyEscapedComponent(Type type) throws Exception {
+        mC1 = type.newInstance("http://developer.android.com/query?q=" + QUERY);
+        // Replace the very last char.
+        mC2 =
+                type.newInstance(
+                        "http://developer.android.com/query?q="
+                                + QUERY.substring(0, QUERY.length() - 3)
+                                + "%AF");
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             mC1.equals(mC2);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java
index 6fe9059..80c4487 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java
@@ -20,26 +20,24 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
-import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class KeyPairGeneratorPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mAlgorithm={0}, mImplementation={1}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {Algorithm.RSA, Implementation.BouncyCastle},
@@ -48,12 +46,6 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public Algorithm mAlgorithm;
-
-    @Parameterized.Parameter(1)
-    public Implementation mImplementation;
-
     public enum Algorithm {
         RSA,
         DSA,
@@ -66,26 +58,25 @@
 
     private String mGeneratorAlgorithm;
     private KeyPairGenerator mGenerator;
-    private SecureRandom mRandom;
 
-    @Before
-    public void setUp() throws Exception {
-        this.mGeneratorAlgorithm = mAlgorithm.toString();
+    public void setUp(Algorithm algorithm, Implementation implementation) throws Exception {
+        this.mGeneratorAlgorithm = algorithm.toString();
 
         final String provider;
-        if (mImplementation == Implementation.BouncyCastle) {
+        if (implementation == Implementation.BouncyCastle) {
             provider = "BC";
         } else {
             provider = "AndroidOpenSSL";
         }
 
         this.mGenerator = KeyPairGenerator.getInstance(mGeneratorAlgorithm, provider);
-        this.mRandom = SecureRandom.getInstance("SHA1PRNG");
         this.mGenerator.initialize(1024);
     }
 
     @Test
-    public void time() throws Exception {
+    @Parameters(method = "getData")
+    public void time(Algorithm algorithm, Implementation implementation) throws Exception {
+        setUp(algorithm, implementation);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             KeyPair keyPair = mGenerator.generateKeyPair();
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java
index 414764d..c9b0cbe 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java
@@ -20,11 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -34,36 +35,34 @@
  *
  * @author Kevin Bourrillion
  */
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class LoopingBackwardsPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mMax={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{2}, {20}, {2000}, {20000000}});
     }
 
-    @Parameterized.Parameter(0)
-    public int mMax;
-
     @Test
-    public void timeForwards() {
+    @Parameters(method = "getData")
+    public void timeForwards(int max) {
         int fake = 0;
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            for (int j = 0; j < mMax; j++) {
+            for (int j = 0; j < max; j++) {
                 fake += j;
             }
         }
     }
 
     @Test
-    public void timeBackwards() {
+    @Parameters(method = "getData")
+    public void timeBackwards(int max) {
         int fake = 0;
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            for (int j = mMax - 1; j >= 0; j--) {
+            for (int j = max - 1; j >= 0; j--) {
                 fake += j;
             }
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
index 279681b..2dc947a 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
@@ -20,24 +20,24 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.nio.ByteBuffer;
 import java.security.MessageDigest;
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class MessageDigestPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mAlgorithm={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {Algorithm.MD5},
@@ -48,9 +48,6 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public Algorithm mAlgorithm;
-
     public String mProvider = "AndroidOpenSSL";
 
     private static final int DATA_SIZE = 8192;
@@ -97,44 +94,44 @@
     };
 
     @Test
-    public void time() throws Exception {
+    @Parameters(method = "getData")
+    public void time(Algorithm algorithm) throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            MessageDigest digest =
-                    MessageDigest.getInstance(mAlgorithm.toString(), mProvider);
+            MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             digest.update(DATA, 0, DATA_SIZE);
             digest.digest();
         }
     }
 
     @Test
-    public void timeLargeArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeLargeArray(Algorithm algorithm) throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            MessageDigest digest =
-                    MessageDigest.getInstance(mAlgorithm.toString(), mProvider);
+            MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             digest.update(LARGE_DATA, 0, LARGE_DATA_SIZE);
             digest.digest();
         }
     }
 
     @Test
-    public void timeSmallChunkOfLargeArray() throws Exception {
+    @Parameters(method = "getData")
+    public void timeSmallChunkOfLargeArray(Algorithm algorithm) throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            MessageDigest digest =
-                    MessageDigest.getInstance(mAlgorithm.toString(), mProvider);
+            MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             digest.update(LARGE_DATA, LARGE_DATA_SIZE / 2, DATA_SIZE);
             digest.digest();
         }
     }
 
     @Test
-    public void timeSmallByteBuffer() throws Exception {
+    @Parameters(method = "getData")
+    public void timeSmallByteBuffer(Algorithm algorithm) throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            MessageDigest digest =
-                    MessageDigest.getInstance(mAlgorithm.toString(), mProvider);
+            MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             SMALL_BUFFER.position(0);
             SMALL_BUFFER.limit(SMALL_BUFFER.capacity());
             digest.update(SMALL_BUFFER);
@@ -143,11 +140,11 @@
     }
 
     @Test
-    public void timeSmallDirectByteBuffer() throws Exception {
+    @Parameters(method = "getData")
+    public void timeSmallDirectByteBuffer(Algorithm algorithm) throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            MessageDigest digest =
-                    MessageDigest.getInstance(mAlgorithm.toString(), mProvider);
+            MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             SMALL_DIRECT_BUFFER.position(0);
             SMALL_DIRECT_BUFFER.limit(SMALL_DIRECT_BUFFER.capacity());
             digest.update(SMALL_DIRECT_BUFFER);
@@ -156,11 +153,11 @@
     }
 
     @Test
-    public void timeLargeByteBuffer() throws Exception {
+    @Parameters(method = "getData")
+    public void timeLargeByteBuffer(Algorithm algorithm) throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            MessageDigest digest =
-                    MessageDigest.getInstance(mAlgorithm.toString(), mProvider);
+            MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             LARGE_BUFFER.position(0);
             LARGE_BUFFER.limit(LARGE_BUFFER.capacity());
             digest.update(LARGE_BUFFER);
@@ -169,11 +166,11 @@
     }
 
     @Test
-    public void timeLargeDirectByteBuffer() throws Exception {
+    @Parameters(method = "getData")
+    public void timeLargeDirectByteBuffer(Algorithm algorithm) throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            MessageDigest digest =
-                    MessageDigest.getInstance(mAlgorithm.toString(), mProvider);
+            MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             LARGE_DIRECT_BUFFER.position(0);
             LARGE_DIRECT_BUFFER.limit(LARGE_DIRECT_BUFFER.capacity());
             digest.update(LARGE_DIRECT_BUFFER);
@@ -182,11 +179,11 @@
     }
 
     @Test
-    public void timeSmallChunkOfLargeByteBuffer() throws Exception {
+    @Parameters(method = "getData")
+    public void timeSmallChunkOfLargeByteBuffer(Algorithm algorithm) throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            MessageDigest digest =
-                    MessageDigest.getInstance(mAlgorithm.toString(), mProvider);
+            MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             LARGE_BUFFER.position(LARGE_BUFFER.capacity() / 2);
             LARGE_BUFFER.limit(LARGE_BUFFER.position() + DATA_SIZE);
             digest.update(LARGE_BUFFER);
@@ -195,11 +192,11 @@
     }
 
     @Test
-    public void timeSmallChunkOfLargeDirectByteBuffer() throws Exception {
+    @Parameters(method = "getData")
+    public void timeSmallChunkOfLargeDirectByteBuffer(Algorithm algorithm) throws Exception {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            MessageDigest digest =
-                    MessageDigest.getInstance(mAlgorithm.toString(), mProvider);
+            MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             LARGE_DIRECT_BUFFER.position(LARGE_DIRECT_BUFFER.capacity() / 2);
             LARGE_DIRECT_BUFFER.limit(LARGE_DIRECT_BUFFER.position() + DATA_SIZE);
             digest.update(LARGE_DIRECT_BUFFER);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java
index 37bd73c..d9d4bb5 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java
@@ -20,17 +20,18 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.concurrent.atomic.AtomicInteger;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class MutableIntPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -96,29 +97,28 @@
         abstract int timeGet(BenchmarkState state);
     }
 
-    @Parameters(name = "mKind={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{Kind.ARRAY}, {Kind.ATOMIC}});
     }
 
-    @Parameterized.Parameter(0)
-    public Kind mKind;
-
     @Test
-    public void timeCreate() {
+    @Parameters(method = "getData")
+    public void timeCreate(Kind kind) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        mKind.timeCreate(state);
+        kind.timeCreate(state);
     }
 
     @Test
-    public void timeIncrement() {
+    @Parameters(method = "getData")
+    public void timeIncrement(Kind kind) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        mKind.timeIncrement(state);
+        kind.timeIncrement(state);
     }
 
     @Test
-    public void timeGet() {
+    @Parameters(method = "getData")
+    public void timeGet(Kind kind) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        mKind.timeGet(state);
+        kind.timeGet(state);
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java
index 8801a56..48450b4 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java
@@ -20,12 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -35,13 +35,12 @@
 import java.util.PriorityQueue;
 import java.util.Random;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class PriorityQueuePerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mQueueSize={0}, mHitRate={1}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {100, 0},
@@ -62,26 +61,19 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public int mQueueSize;
-
-    @Parameterized.Parameter(1)
-    public int mHitRate;
-
     private PriorityQueue<Integer> mPq;
     private PriorityQueue<Integer> mUsepq;
     private List<Integer> mSeekElements;
     private Random mRandom = new Random(189279387L);
 
-    @Before
-    public void setUp() throws Exception {
+    public void setUp(int queueSize, int hitRate) throws Exception {
         mPq = new PriorityQueue<Integer>();
         mUsepq = new PriorityQueue<Integer>();
         mSeekElements = new ArrayList<Integer>();
         List<Integer> allElements = new ArrayList<Integer>();
-        int numShared = (int) (mQueueSize * ((double) mHitRate / 100));
-        // the total number of elements we require to engineer a hit rate of mHitRate%
-        int totalElements = 2 * mQueueSize - numShared;
+        int numShared = (int) (queueSize * ((double) hitRate / 100));
+        // the total number of elements we require to engineer a hit rate of hitRate%
+        int totalElements = 2 * queueSize - numShared;
         for (int i = 0; i < totalElements; i++) {
             allElements.add(i);
         }
@@ -93,11 +85,11 @@
             mSeekElements.add(allElements.get(i));
         }
         // add priority queue only elements (these won't be touched)
-        for (int i = numShared; i < mQueueSize; i++) {
+        for (int i = numShared; i < queueSize; i++) {
             mPq.add(allElements.get(i));
         }
         // add non-priority queue elements (these will be misses)
-        for (int i = mQueueSize; i < totalElements; i++) {
+        for (int i = queueSize; i < totalElements; i++) {
             mSeekElements.add(allElements.get(i));
         }
         mUsepq = new PriorityQueue<Integer>(mPq);
@@ -107,16 +99,18 @@
     }
 
     @Test
-    public void timeRemove() {
+    @Parameters(method = "getData")
+    public void timeRemove(int queueSize, int hitRate) throws Exception {
+        setUp(queueSize, hitRate);
         boolean fake = false;
         int elementsSize = mSeekElements.size();
         // At most allow the queue to empty 10%.
-        int resizingThreshold = mQueueSize / 10;
+        int resizingThreshold = queueSize / 10;
         int i = 0;
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             // Reset queue every so often. This will be called more often for smaller
-            // mQueueSizes, but since a copy is linear, it will also cost proportionally
+            // queueSizes, but since a copy is linear, it will also cost proportionally
             // less, and hopefully it will approximately balance out.
             if (++i % resizingThreshold == 0) {
                 mUsepq = new PriorityQueue<Integer>(mPq);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java
index 42dc581..5ad62de 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java
@@ -20,11 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -32,7 +33,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class SchemePrefixPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -85,19 +86,16 @@
         abstract String execute(String spec);
     }
 
-    @Parameters(name = "mStrategy={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{Strategy.REGEX}, {Strategy.JAVA}});
     }
 
-    @Parameterized.Parameter(0)
-    public Strategy mStrategy;
-
     @Test
-    public void timeSchemePrefix() {
+    @Parameters(method = "getData")
+    public void timeSchemePrefix(Strategy strategy) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStrategy.execute("http://android.com");
+            strategy.execute("http://android.com");
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java
index 96e7cb2..a9a0788 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java
@@ -19,12 +19,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
@@ -37,13 +37,12 @@
 import java.util.Map;
 
 /** Tests RSA and DSA mSignature creation and verification. */
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class SignaturePerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mAlgorithm={0}, mImplementation={1}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {Algorithm.MD5WithRSA, Implementation.OpenSSL},
@@ -55,12 +54,6 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public Algorithm mAlgorithm;
-
-    @Parameterized.Parameter(1)
-    public Implementation mImplementation;
-
     private static final int DATA_SIZE = 8192;
     private static final byte[] DATA = new byte[DATA_SIZE];
 
@@ -94,9 +87,8 @@
     private PrivateKey mPrivateKey;
     private PublicKey mPublicKey;
 
-    @Before
-    public void setUp() throws Exception {
-        this.mSignatureAlgorithm = mAlgorithm.toString();
+    public void setUp(Algorithm algorithm) throws Exception {
+        this.mSignatureAlgorithm = algorithm.toString();
 
         String keyAlgorithm =
                 mSignatureAlgorithm.substring(
@@ -121,11 +113,13 @@
     }
 
     @Test
-    public void timeSign() throws Exception {
+    @Parameters(method = "getData")
+    public void timeSign(Algorithm algorithm, Implementation implementation) throws Exception {
+        setUp(algorithm);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             Signature signer;
-            switch (mImplementation) {
+            switch (implementation) {
                 case OpenSSL:
                     signer = Signature.getInstance(mSignatureAlgorithm, "AndroidOpenSSL");
                     break;
@@ -133,7 +127,7 @@
                     signer = Signature.getInstance(mSignatureAlgorithm, "BC");
                     break;
                 default:
-                    throw new RuntimeException(mImplementation.toString());
+                    throw new RuntimeException(implementation.toString());
             }
             signer.initSign(mPrivateKey);
             signer.update(DATA);
@@ -142,11 +136,13 @@
     }
 
     @Test
-    public void timeVerify() throws Exception {
+    @Parameters(method = "getData")
+    public void timeVerify(Algorithm algorithm, Implementation implementation) throws Exception {
+        setUp(algorithm);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             Signature verifier;
-            switch (mImplementation) {
+            switch (implementation) {
                 case OpenSSL:
                     verifier = Signature.getInstance(mSignatureAlgorithm, "AndroidOpenSSL");
                     break;
@@ -154,7 +150,7 @@
                     verifier = Signature.getInstance(mSignatureAlgorithm, "BC");
                     break;
                 default:
-                    throw new RuntimeException(mImplementation.toString());
+                    throw new RuntimeException(implementation.toString());
             }
             verifier.initVerify(mPublicKey);
             verifier.update(DATA);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java
index 02194b1..36db014 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java
@@ -20,16 +20,17 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class StringPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -46,8 +47,7 @@
         }
     }
 
-    @Parameters(name = "mStringLengths={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {StringLengths.EIGHT_KI},
@@ -57,9 +57,6 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public StringLengths mStringLengths;
-
     private static String makeString(int length) {
         StringBuilder result = new StringBuilder(length);
         for (int i = 0; i < length; ++i) {
@@ -69,10 +66,11 @@
     }
 
     @Test
-    public void timeHashCode() {
+    @Parameters(method = "getData")
+    public void timeHashCode(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.hashCode();
+            stringLengths.mValue.hashCode();
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java
index b0d1ee4..5b4423a 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java
@@ -20,16 +20,17 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class StringReplaceAllPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -69,8 +70,7 @@
         return stringBuilder.toString();
     }
 
-    @Parameters(name = "mStringLengths={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {StringLengths.BOOT_IMAGE},
@@ -82,30 +82,30 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public StringLengths mStringLengths;
-
     @Test
-    public void timeReplaceAllTrivialPatternNonExistent() {
+    @Parameters(method = "getData")
+    public void timeReplaceAllTrivialPatternNonExistent(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.replaceAll("fish", "0");
+            stringLengths.mValue.replaceAll("fish", "0");
         }
     }
 
     @Test
-    public void timeReplaceTrivialPatternAllRepeated() {
+    @Parameters(method = "getData")
+    public void timeReplaceTrivialPatternAllRepeated(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.replaceAll("jklm", "0");
+            stringLengths.mValue.replaceAll("jklm", "0");
         }
     }
 
     @Test
-    public void timeReplaceAllTrivialPatternSingleOccurrence() {
+    @Parameters(method = "getData")
+    public void timeReplaceAllTrivialPatternSingleOccurrence(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.replaceAll("qrst", "0");
+            stringLengths.mValue.replaceAll("qrst", "0");
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java
index d2e657a..4d5c792 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java
@@ -20,16 +20,17 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class StringReplacePerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -64,8 +65,7 @@
         return stringBuilder.toString();
     }
 
-    @Parameters(name = "mStringLengths={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {StringLengths.EMPTY},
@@ -76,54 +76,57 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public StringLengths mStringLengths;
-
     @Test
-    public void timeReplaceCharNonExistent() {
+    @Parameters(method = "getData")
+    public void timeReplaceCharNonExistent(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.replace('z', '0');
+            stringLengths.mValue.replace('z', '0');
         }
     }
 
     @Test
-    public void timeReplaceCharRepeated() {
+    @Parameters(method = "getData")
+    public void timeReplaceCharRepeated(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.replace('a', '0');
+            stringLengths.mValue.replace('a', '0');
         }
     }
 
     @Test
-    public void timeReplaceSingleChar() {
+    @Parameters(method = "getData")
+    public void timeReplaceSingleChar(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.replace('q', '0');
+            stringLengths.mValue.replace('q', '0');
         }
     }
 
     @Test
-    public void timeReplaceSequenceNonExistent() {
+    @Parameters(method = "getData")
+    public void timeReplaceSequenceNonExistent(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.replace("fish", "0");
+            stringLengths.mValue.replace("fish", "0");
         }
     }
 
     @Test
-    public void timeReplaceSequenceRepeated() {
+    @Parameters(method = "getData")
+    public void timeReplaceSequenceRepeated(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.replace("jklm", "0");
+            stringLengths.mValue.replace("jklm", "0");
         }
     }
 
     @Test
-    public void timeReplaceSingleSequence() {
+    @Parameters(method = "getData")
+    public void timeReplaceSingleSequence(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.replace("qrst", "0");
+            stringLengths.mValue.replace("qrst", "0");
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java
index 1efc188..c004d95 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java
@@ -20,17 +20,18 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class StringToBytesPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -53,8 +54,7 @@
         }
     }
 
-    @Parameters(name = "mStringLengths={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {StringLengths.EMPTY},
@@ -69,9 +69,6 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public StringLengths mStringLengths;
-
     private static String makeString(int length) {
         char[] chars = new char[length];
         for (int i = 0; i < length; ++i) {
@@ -89,26 +86,29 @@
     }
 
     @Test
-    public void timeGetBytesUtf8() {
+    @Parameters(method = "getData")
+    public void timeGetBytesUtf8(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.getBytes(StandardCharsets.UTF_8);
+            stringLengths.mValue.getBytes(StandardCharsets.UTF_8);
         }
     }
 
     @Test
-    public void timeGetBytesIso88591() {
+    @Parameters(method = "getData")
+    public void timeGetBytesIso88591(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.getBytes(StandardCharsets.ISO_8859_1);
+            stringLengths.mValue.getBytes(StandardCharsets.ISO_8859_1);
         }
     }
 
     @Test
-    public void timeGetBytesAscii() {
+    @Parameters(method = "getData")
+    public void timeGetBytesAscii(StringLengths stringLengths) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            mStringLengths.mValue.getBytes(StandardCharsets.US_ASCII);
+            stringLengths.mValue.getBytes(StandardCharsets.US_ASCII);
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java
index b01948a..15516fc 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java
@@ -20,22 +20,22 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.Collection;
 
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class StringToRealPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mString={0}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {"NaN"},
@@ -49,22 +49,21 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public String mString;
-
     @Test
-    public void timeFloat_parseFloat() {
+    @Parameters(method = "getData")
+    public void timeFloat_parseFloat(String string) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            Float.parseFloat(mString);
+            Float.parseFloat(string);
         }
     }
 
     @Test
-    public void timeDouble_parseDouble() {
+    @Parameters(method = "getData")
+    public void timeDouble_parseDouble(String string) {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            Double.parseDouble(mString);
+            Double.parseDouble(string);
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java
index 2ea834d..ae1e8bc 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java
@@ -20,12 +20,12 @@
 import android.perftests.utils.PerfStatusReporter;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.junit.Before;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 import org.xml.sax.InputSource;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserFactory;
@@ -38,13 +38,12 @@
 import javax.xml.parsers.DocumentBuilderFactory;
 
 // http://code.google.com/p/android/issues/detail?id=18102
-@RunWith(Parameterized.class)
+@RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class XMLEntitiesPerfTest {
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameters(name = "mLength={0}, mEntityFraction={1}")
-    public static Collection<Object[]> data() {
+    public static Collection<Object[]> getData() {
         return Arrays.asList(
                 new Object[][] {
                     {10, 0},
@@ -59,29 +58,22 @@
                 });
     }
 
-    @Parameterized.Parameter(0)
-    public int mLength;
-
-    @Parameterized.Parameter(1)
-    public float mEntityFraction;
-
     private XmlPullParserFactory mXmlPullParserFactory;
     private DocumentBuilderFactory mDocumentBuilderFactory;
 
     /** a string like {@code <doc>&amp;&amp;++</doc>}. */
     private String mXml;
 
-    @Before
-    public void setUp() throws Exception {
+    public void setUp(int length, float entityFraction) throws Exception {
         mXmlPullParserFactory = XmlPullParserFactory.newInstance();
         mDocumentBuilderFactory = DocumentBuilderFactory.newInstance();
 
         StringBuilder xmlBuilder = new StringBuilder();
         xmlBuilder.append("<doc>");
-        for (int i = 0; i < (mLength * mEntityFraction); i++) {
+        for (int i = 0; i < (length * entityFraction); i++) {
             xmlBuilder.append("&amp;");
         }
-        while (xmlBuilder.length() < mLength) {
+        while (xmlBuilder.length() < length) {
             xmlBuilder.append("+");
         }
         xmlBuilder.append("</doc>");
@@ -89,7 +81,9 @@
     }
 
     @Test
-    public void timeXmlParser() throws Exception {
+    @Parameters(method = "getData")
+    public void timeXmlParser(int length, float entityFraction) throws Exception {
+        setUp(length, entityFraction);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             XmlPullParser parser = mXmlPullParserFactory.newPullParser();
@@ -101,7 +95,9 @@
     }
 
     @Test
-    public void timeDocumentBuilder() throws Exception {
+    @Parameters(method = "getData")
+    public void timeDocumentBuilder(int length, float entityFraction) throws Exception {
+        setUp(length, entityFraction);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             DocumentBuilder documentBuilder = mDocumentBuilderFactory.newDocumentBuilder();
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 5a445d4..dade7c3 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -309,7 +309,7 @@
         /**
          * Callback method that is invoked by the system when the alarm time is reached.
          */
-        public void onAlarm();
+        void onAlarm();
     }
 
     final class ListenerWrapper extends IAlarmListener.Stub implements Runnable {
@@ -453,7 +453,7 @@
      * @see #RTC
      * @see #RTC_WAKEUP
      */
-    public void set(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
+    public void set(@AlarmType int type, long triggerAtMillis, @NonNull PendingIntent operation) {
         setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null,
                 (Handler) null, null, null);
     }
@@ -480,8 +480,8 @@
      * @param targetHandler {@link Handler} on which to execute the listener's onAlarm()
      *         callback, or {@code null} to run that callback on the main looper.
      */
-    public void set(@AlarmType int type, long triggerAtMillis, String tag, OnAlarmListener listener,
-            Handler targetHandler) {
+    public void set(@AlarmType int type, long triggerAtMillis, @Nullable String tag,
+            @NonNull OnAlarmListener listener, @Nullable Handler targetHandler) {
         setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, null, listener, tag,
                 targetHandler, null, null);
     }
@@ -546,7 +546,7 @@
      * @see Intent#EXTRA_ALARM_COUNT
      */
     public void setRepeating(@AlarmType int type, long triggerAtMillis,
-            long intervalMillis, PendingIntent operation) {
+            long intervalMillis, @NonNull PendingIntent operation) {
         setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation,
                 null, null, (Handler) null, null, null);
     }
@@ -602,7 +602,7 @@
      * @see #RTC_WAKEUP
      */
     public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
-            PendingIntent operation) {
+            @NonNull PendingIntent operation) {
         setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, operation,
                 null, null, (Handler) null, null, null);
     }
@@ -625,12 +625,62 @@
      * @see #setWindow(int, long, long, PendingIntent)
      */
     public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
-            String tag, OnAlarmListener listener, Handler targetHandler) {
+            @Nullable String tag, @NonNull OnAlarmListener listener,
+            @Nullable Handler targetHandler) {
         setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, null, listener, tag,
                 targetHandler, null, null);
     }
 
     /**
+     * Direct callback version of {@link #setWindow(int, long, long, PendingIntent)}.  Rather
+     * than supplying a PendingIntent to be sent when the alarm time is reached, this variant
+     * supplies an {@link OnAlarmListener} instance that will be invoked at that time.
+     * <p>
+     * The OnAlarmListener {@link OnAlarmListener#onAlarm() onAlarm()} method will be
+     * invoked via the specified target Executor.
+     *
+     * <p>
+     * Note: Starting with API {@link Build.VERSION_CODES#S}, apps should not pass in a window of
+     * less than 10 minutes. The system will try its best to accommodate smaller windows if the
+     * alarm is supposed to fire in the near future, but there are no guarantees and the app should
+     * expect any window smaller than 10 minutes to get elongated to 10 minutes.
+     *
+     * @see #setWindow(int, long, long, PendingIntent)
+     */
+    public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
+            @Nullable String tag, @NonNull Executor executor, @NonNull OnAlarmListener listener) {
+        setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, null, listener, tag,
+                executor, null, null);
+    }
+
+    /**
+     * Direct callback version of {@link #setWindow(int, long, long, PendingIntent)}.  Rather
+     * than supplying a PendingIntent to be sent when the alarm time is reached, this variant
+     * supplies an {@link OnAlarmListener} instance that will be invoked at that time.
+     * <p>
+     * The OnAlarmListener {@link OnAlarmListener#onAlarm() onAlarm()} method will be
+     * invoked via the specified target Executor.
+     *
+     * <p>
+     * Note: Starting with API {@link Build.VERSION_CODES#S}, apps should not pass in a window of
+     * less than 10 minutes. The system will try its best to accommodate smaller windows if the
+     * alarm is supposed to fire in the near future, but there are no guarantees and the app should
+     * expect any window smaller than 10 minutes to get elongated to 10 minutes.
+     *
+     * @see #setWindow(int, long, long, PendingIntent)
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
+            @Nullable String tag, @NonNull Executor executor, @Nullable WorkSource workSource,
+            @NonNull OnAlarmListener listener) {
+        setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, null, listener, tag,
+                executor, workSource, null);
+    }
+
+    /**
      * Schedule an alarm that is prioritized by the system while the device is in power saving modes
      * such as battery saver and device idle (doze).
      *
@@ -725,7 +775,8 @@
      * @see Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM
      */
     @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
-    public void setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
+    public void setExact(@AlarmType int type, long triggerAtMillis,
+            @NonNull PendingIntent operation) {
         setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, (Handler) null,
                 null, null);
     }
@@ -756,8 +807,8 @@
      * @see Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM
      */
     @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
-    public void setExact(@AlarmType int type, long triggerAtMillis, String tag,
-            OnAlarmListener listener, Handler targetHandler) {
+    public void setExact(@AlarmType int type, long triggerAtMillis, @Nullable String tag,
+            @NonNull OnAlarmListener listener, @Nullable Handler targetHandler) {
         setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, null, listener, tag,
                 targetHandler, null, null);
     }
@@ -767,8 +818,8 @@
      * the given time.
      * @hide
      */
-    public void setIdleUntil(@AlarmType int type, long triggerAtMillis, String tag,
-            OnAlarmListener listener, Handler targetHandler) {
+    public void setIdleUntil(@AlarmType int type, long triggerAtMillis, @Nullable String tag,
+            @NonNull OnAlarmListener listener, @Nullable Handler targetHandler) {
         setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_IDLE_UNTIL, null,
                 listener, tag, targetHandler, null, null);
     }
@@ -828,7 +879,7 @@
      * @see Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM
      */
     @RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
-    public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
+    public void setAlarmClock(@NonNull AlarmClockInfo info, @NonNull PendingIntent operation) {
         setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
                 null, null, (Handler) null, null, info);
     }
@@ -837,7 +888,8 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public void set(@AlarmType int type, long triggerAtMillis, long windowMillis,
-            long intervalMillis, PendingIntent operation, WorkSource workSource) {
+            long intervalMillis, @NonNull PendingIntent operation,
+            @Nullable WorkSource workSource) {
         setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, null, null,
                 (Handler) null, workSource, null);
     }
@@ -854,8 +906,8 @@
      */
     @UnsupportedAppUsage
     public void set(@AlarmType int type, long triggerAtMillis, long windowMillis,
-            long intervalMillis, String tag, OnAlarmListener listener, Handler targetHandler,
-            WorkSource workSource) {
+            long intervalMillis, @Nullable String tag, @NonNull OnAlarmListener listener,
+            @Nullable Handler targetHandler, @Nullable WorkSource workSource) {
         setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, null, listener, tag,
                 targetHandler, workSource, null);
     }
@@ -873,8 +925,8 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public void set(@AlarmType int type, long triggerAtMillis, long windowMillis,
-            long intervalMillis, OnAlarmListener listener, Handler targetHandler,
-            WorkSource workSource) {
+            long intervalMillis, @NonNull OnAlarmListener listener, @Nullable Handler targetHandler,
+            @Nullable WorkSource workSource) {
         setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, null, listener, null,
                 targetHandler, workSource, null);
     }
@@ -1072,7 +1124,7 @@
      * @see Intent#EXTRA_ALARM_COUNT
      */
     public void setInexactRepeating(@AlarmType int type, long triggerAtMillis,
-            long intervalMillis, PendingIntent operation) {
+            long intervalMillis, @NonNull PendingIntent operation) {
         setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null,
                 null, (Handler) null, null, null);
     }
@@ -1122,7 +1174,7 @@
      * @see #RTC_WAKEUP
      */
     public void setAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
-            PendingIntent operation) {
+            @NonNull PendingIntent operation) {
         setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE,
                 operation, null, null, (Handler) null, null, null);
     }
@@ -1195,12 +1247,46 @@
      */
     @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
     public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
-            PendingIntent operation) {
+            @NonNull PendingIntent operation) {
         setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
                 null, null, (Handler) null, null, null);
     }
 
     /**
+     * Like {@link #setExact(int, long, String, Executor, WorkSource, OnAlarmListener)}, but this
+     * alarm will be allowed to execute even when the system is in low-power idle modes.
+     *
+     * <p> See {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} for more details.
+     *
+     * @param type            type of alarm
+     * @param triggerAtMillis The exact time in milliseconds, that the alarm should be delivered,
+     *                        expressed in the appropriate clock's units (depending on the alarm
+     *                        type).
+     * @param listener        {@link OnAlarmListener} instance whose
+     *                        {@link OnAlarmListener#onAlarm() onAlarm()} method will be called when
+     *                        the alarm time is reached.
+     * @param executor        The {@link Executor} on which to execute the listener's onAlarm()
+     *                        callback.
+     * @param tag             Optional. A string tag used to identify this alarm in logs and
+     *                        battery-attribution.
+     * @param workSource      A {@link WorkSource} object to attribute this alarm to the app that
+     *                        requested this work.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {
+            Manifest.permission.UPDATE_DEVICE_STATS,
+            Manifest.permission.SCHEDULE_EXACT_ALARM}, conditional = true)
+    public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
+            @Nullable String tag, @NonNull Executor executor, @Nullable WorkSource workSource,
+            @NonNull OnAlarmListener listener) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(listener);
+        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, null, listener, tag,
+                executor, workSource, null);
+    }
+
+    /**
      * Remove any alarms with a matching {@link Intent}.
      * Any alarm, of any type, whose Intent matches this one (as defined by
      * {@link Intent#filterEquals}), will be canceled.
@@ -1210,7 +1296,7 @@
      *
      * @see #set
      */
-    public void cancel(PendingIntent operation) {
+    public void cancel(@NonNull PendingIntent operation) {
         if (operation == null) {
             final String msg = "cancel() called with a null PendingIntent";
             if (mTargetSdkVersion >= Build.VERSION_CODES.N) {
@@ -1233,7 +1319,7 @@
      *
      * @param listener OnAlarmListener instance that is the target of a currently-set alarm.
      */
-    public void cancel(OnAlarmListener listener) {
+    public void cancel(@NonNull OnAlarmListener listener) {
         if (listener == null) {
             throw new NullPointerException("cancel() called with a null OnAlarmListener");
         }
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index 299ad66..4a3a6d9 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -113,7 +113,9 @@
     /** @hide */
     public static final String KEY_AM_INITIAL_CONSUMPTION_LIMIT = "am_initial_consumption_limit";
     /** @hide */
-    public static final String KEY_AM_HARD_CONSUMPTION_LIMIT = "am_hard_consumption_limit";
+    public static final String KEY_AM_MIN_CONSUMPTION_LIMIT = "am_minimum_consumption_limit";
+    /** @hide */
+    public static final String KEY_AM_MAX_CONSUMPTION_LIMIT = "am_maximum_consumption_limit";
     // TODO: Add AlarmManager modifier keys
     /** @hide */
     public static final String KEY_AM_REWARD_TOP_ACTIVITY_INSTANT =
@@ -242,7 +244,9 @@
     /** @hide */
     public static final String KEY_JS_INITIAL_CONSUMPTION_LIMIT = "js_initial_consumption_limit";
     /** @hide */
-    public static final String KEY_JS_HARD_CONSUMPTION_LIMIT = "js_hard_consumption_limit";
+    public static final String KEY_JS_MIN_CONSUMPTION_LIMIT = "js_minimum_consumption_limit";
+    /** @hide */
+    public static final String KEY_JS_MAX_CONSUMPTION_LIMIT = "js_maximum_consumption_limit";
     // TODO: Add JobScheduler modifier keys
     /** @hide */
     public static final String KEY_JS_REWARD_APP_INSTALL_INSTANT =
@@ -371,7 +375,9 @@
     /** @hide */
     public static final long DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES = arcToCake(2880);
     /** @hide */
-    public static final long DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES = arcToCake(15_000);
+    public static final long DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES = arcToCake(1440);
+    /** @hide */
+    public static final long DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES = arcToCake(15_000);
     // TODO: add AlarmManager modifier default values
     /** @hide */
     public static final long DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES = arcToCake(0);
@@ -478,8 +484,10 @@
     /** @hide */
     public static final long DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES = arcToCake(29_000);
     /** @hide */
-    // TODO: set hard limit based on device type (phone vs tablet vs etc) + battery size
-    public static final long DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES = arcToCake(250_000);
+    public static final long DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES = arcToCake(17_000);
+    /** @hide */
+    // TODO: set maximum limit based on device type (phone vs tablet vs etc) + battery size
+    public static final long DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES = arcToCake(250_000);
     // TODO: add JobScheduler modifier default values
     /** @hide */
     public static final long DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES = arcToCake(408);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 20bca35..52dc01b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -460,14 +460,14 @@
                     if (mPowerManager != null && mPowerManager.isDeviceIdleMode()) {
                         synchronized (mLock) {
                             stopUnexemptedJobsForDoze();
-                            stopLongRunningJobsLocked("deep doze");
+                            stopOvertimeJobsLocked("deep doze");
                         }
                     }
                     break;
                 case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
                     if (mPowerManager != null && mPowerManager.isPowerSaveMode()) {
                         synchronized (mLock) {
-                            stopLongRunningJobsLocked("battery saver");
+                            stopOvertimeJobsLocked("battery saver");
                         }
                     }
                     break;
@@ -555,7 +555,7 @@
      * execution guarantee.
      */
     @GuardedBy("mLock")
-    boolean isJobLongRunningLocked(@NonNull JobStatus job) {
+    boolean isJobInOvertimeLocked(@NonNull JobStatus job) {
         if (!mRunningJobs.contains(job)) {
             return false;
         }
@@ -1043,7 +1043,7 @@
     }
 
     @GuardedBy("mLock")
-    private void stopLongRunningJobsLocked(@NonNull String debugReason) {
+    private void stopOvertimeJobsLocked(@NonNull String debugReason) {
         for (int i = 0; i < mActiveServices.size(); ++i) {
             final JobServiceContext jsc = mActiveServices.get(i);
             final JobStatus jobStatus = jsc.getRunningJobLocked();
@@ -1060,7 +1060,7 @@
      * restricted by the given {@link JobRestriction}.
      */
     @GuardedBy("mLock")
-    void maybeStopLongRunningJobsLocked(@NonNull JobRestriction restriction) {
+    void maybeStopOvertimeJobsLocked(@NonNull JobRestriction restriction) {
         for (int i = mActiveServices.size() - 1; i >= 0; --i) {
             final JobServiceContext jsc = mActiveServices.get(i);
             final JobStatus jobStatus = jsc.getRunningJobLocked();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index d28ebde..d9fb318 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -432,6 +432,7 @@
                             break;
                         case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS:
                         case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS:
+                        case Constants.KEY_SYSTEM_STOP_TO_FAILURE_RATIO:
                             mConstants.updateBackoffConstantsLocked();
                             break;
                         case Constants.KEY_CONN_CONGESTION_DELAY_FRAC:
@@ -509,6 +510,8 @@
 
         private static final String KEY_MIN_LINEAR_BACKOFF_TIME_MS = "min_linear_backoff_time_ms";
         private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms";
+        private static final String KEY_SYSTEM_STOP_TO_FAILURE_RATIO =
+                "system_stop_to_failure_ratio";
         private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
         private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
         private static final String KEY_CONN_USE_CELL_SIGNAL_STRENGTH =
@@ -540,6 +543,7 @@
         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
         private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
+        private static final int DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO = 3;
         private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
         private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
         private static final boolean DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH = true;
@@ -589,6 +593,11 @@
          * The minimum backoff time to allow for exponential backoff.
          */
         long MIN_EXP_BACKOFF_TIME_MS = DEFAULT_MIN_EXP_BACKOFF_TIME_MS;
+        /**
+         * The ratio to use to convert number of times a job was stopped by JobScheduler to an
+         * incremental failure in the backoff policy calculation.
+         */
+        int SYSTEM_STOP_TO_FAILURE_RATIO = DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO;
 
         /**
          * The fraction of a job's running window that must pass before we
@@ -700,6 +709,9 @@
             MIN_EXP_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                     KEY_MIN_EXP_BACKOFF_TIME_MS,
                     DEFAULT_MIN_EXP_BACKOFF_TIME_MS);
+            SYSTEM_STOP_TO_FAILURE_RATIO = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    KEY_SYSTEM_STOP_TO_FAILURE_RATIO,
+                    DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO);
         }
 
         private void updateConnectivityConstantsLocked() {
@@ -797,6 +809,7 @@
 
             pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println();
             pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println();
+            pw.print(KEY_SYSTEM_STOP_TO_FAILURE_RATIO, SYSTEM_STOP_TO_FAILURE_RATIO).println();
             pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
             pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
             pw.print(KEY_CONN_USE_CELL_SIGNAL_STRENGTH, CONN_USE_CELL_SIGNAL_STRENGTH).println();
@@ -1277,7 +1290,7 @@
                     jobStatus.getJob().isPrefetch(),
                     jobStatus.getJob().getPriority(),
                     jobStatus.getEffectivePriority(),
-                    jobStatus.getNumFailures());
+                    jobStatus.getNumPreviousAttempts());
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
@@ -1476,7 +1489,7 @@
                     cancelled.getJob().isPrefetch(),
                     cancelled.getJob().getPriority(),
                     cancelled.getEffectivePriority(),
-                    cancelled.getNumFailures());
+                    cancelled.getNumPreviousAttempts());
         }
         // If this is a replacement, bring in the new version of the job
         if (incomingJob != null) {
@@ -1820,12 +1833,13 @@
             Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
         }
         jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
-        final boolean update = mJobs.add(jobStatus);
+        final boolean update = lastJob != null;
+        mJobs.add(jobStatus);
         if (mReadyToRock) {
             for (int i = 0; i < mControllers.size(); i++) {
                 StateController controller = mControllers.get(i);
                 if (update) {
-                    controller.maybeStopTrackingJobLocked(jobStatus, null, true);
+                    controller.maybeStopTrackingJobLocked(jobStatus, null);
                 }
                 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
             }
@@ -1858,7 +1872,7 @@
         if (mReadyToRock) {
             for (int i = 0; i < mControllers.size(); i++) {
                 StateController controller = mControllers.get(i);
-                controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
+                controller.maybeStopTrackingJobLocked(jobStatus, incomingJob);
             }
         }
         return removed;
@@ -1870,10 +1884,10 @@
         return mConcurrencyManager.isJobRunningLocked(job);
     }
 
-    /** @see JobConcurrencyManager#isJobLongRunningLocked(JobStatus) */
+    /** @see JobConcurrencyManager#isJobInOvertimeLocked(JobStatus) */
     @GuardedBy("mLock")
-    public boolean isLongRunningLocked(JobStatus job) {
-        return mConcurrencyManager.isJobLongRunningLocked(job);
+    public boolean isJobInOvertimeLocked(JobStatus job) {
+        return mConcurrencyManager.isJobInOvertimeLocked(job);
     }
 
     private void noteJobPending(JobStatus job) {
@@ -1903,7 +1917,7 @@
      * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
      * specify an override deadline on a failed job (the failed job will run even though it's not
      * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
-     * ready job with {@link JobStatus#getNumFailures()} > 0 will be executed.
+     * ready job with {@link JobStatus#getNumPreviousAttempts()} > 0 will be executed.
      *
      * @param failureToReschedule Provided job status that we will reschedule.
      * @return A newly instantiated JobStatus with the same constraints as the last job except
@@ -1911,12 +1925,24 @@
      * @see #maybeQueueReadyJobsForExecutionLocked
      */
     @VisibleForTesting
-    JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
+    JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule,
+            int internalStopReason) {
         final long elapsedNowMillis = sElapsedRealtimeClock.millis();
         final JobInfo job = failureToReschedule.getJob();
 
         final long initialBackoffMillis = job.getInitialBackoffMillis();
-        final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
+        int numFailures = failureToReschedule.getNumFailures();
+        int numSystemStops = failureToReschedule.getNumSystemStops();
+        // We should back off slowly if JobScheduler keeps stopping the job,
+        // but back off immediately if the issue appeared to be the app's fault.
+        if (internalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH
+                || internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT) {
+            numFailures++;
+        } else {
+            numSystemStops++;
+        }
+        final int backoffAttempts = Math.max(1,
+                numFailures + numSystemStops / mConstants.SYSTEM_STOP_TO_FAILURE_RATIO);
         long delayMillis;
 
         switch (job.getBackoffPolicy()) {
@@ -1943,7 +1969,7 @@
                 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
         JobStatus newJob = new JobStatus(failureToReschedule,
                 elapsedNowMillis + delayMillis,
-                JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
+                JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops,
                 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
         if (job.isPeriodic()) {
             newJob.setOriginalLatestRunTimeElapsed(
@@ -2034,7 +2060,7 @@
                     + newLatestRuntimeElapsed);
             return new JobStatus(periodicToReschedule,
                     elapsedNow + period - flex, elapsedNow + period,
-                    0 /* backoffAttempt */,
+                    0 /* numFailures */, 0 /* numSystemStops */,
                     sSystemClock.millis() /* lastSuccessfulRunTime */,
                     periodicToReschedule.getLastFailedRunTime());
         }
@@ -2049,7 +2075,7 @@
         }
         return new JobStatus(periodicToReschedule,
                 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
-                0 /* backoffAttempt */,
+                0 /* numFailures */, 0 /* numSystemStops */,
                 sSystemClock.millis() /* lastSuccessfulRunTime */,
                 periodicToReschedule.getLastFailedRunTime());
     }
@@ -2093,7 +2119,7 @@
         // job so we can transfer any appropriate state over from the previous job when
         // we stop it.
         final JobStatus rescheduledJob = needsReschedule
-                ? getRescheduleJobForFailureLocked(jobStatus) : null;
+                ? getRescheduleJobForFailureLocked(jobStatus, debugStopReason) : null;
         if (rescheduledJob != null
                 && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT
                 || debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) {
@@ -2155,11 +2181,11 @@
 
     @Override
     public void onRestrictionStateChanged(@NonNull JobRestriction restriction,
-            boolean stopLongRunningJobs) {
+            boolean stopOvertimeJobs) {
         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
-        if (stopLongRunningJobs) {
+        if (stopOvertimeJobs) {
             synchronized (mLock) {
-                mConcurrencyManager.maybeStopLongRunningJobsLocked(restriction);
+                mConcurrencyManager.maybeStopOvertimeJobsLocked(restriction);
             }
         }
     }
@@ -2427,7 +2453,7 @@
                     shouldForceBatchJob =
                             mPrefetchController.getNextEstimatedLaunchTimeLocked(job)
                                     > relativelySoonCutoffTime;
-                } else if (job.getNumFailures() > 0) {
+                } else if (job.getNumPreviousAttempts() > 0) {
                     shouldForceBatchJob = false;
                 } else {
                     final long nowElapsed = sElapsedRealtimeClock.millis();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index d6456f0..9e3f19d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -363,7 +363,7 @@
                     job.getJob().isPrefetch(),
                     job.getJob().getPriority(),
                     job.getEffectivePriority(),
-                    job.getNumFailures());
+                    job.getNumPreviousAttempts());
             if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
                 // Use the context's ID to distinguish traces since there'll only be one job
                 // running per context.
@@ -1032,7 +1032,7 @@
                 completedJob.getJob().isPrefetch(),
                 completedJob.getJob().getPriority(),
                 completedJob.getEffectivePriority(),
-                completedJob.getNumFailures());
+                completedJob.getNumPreviousAttempts());
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
             Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
                     completedJob.getTag(), getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index 22b0968..68cb049 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -194,7 +194,7 @@
                         convertRtcBoundsToElapsed(utcTimes, elapsedNow);
                 JobStatus newJob = new JobStatus(job,
                         elapsedRuntimes.first, elapsedRuntimes.second,
-                        0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime());
+                        0, 0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime());
                 newJob.prepareLocked();
                 toAdd.add(newJob);
                 toRemove.add(job);
@@ -203,13 +203,12 @@
     }
 
     /**
-     * Add a job to the master list, persisting it if necessary. If the JobStatus already exists,
-     * it will be replaced.
+     * Add a job to the master list, persisting it if necessary.
+     * Similar jobs to the new job will not be removed.
+     *
      * @param jobStatus Job to add.
-     * @return Whether or not an equivalent JobStatus was replaced by this operation.
      */
-    public boolean add(JobStatus jobStatus) {
-        boolean replaced = mJobSet.remove(jobStatus);
+    public void add(JobStatus jobStatus) {
         mJobSet.add(jobStatus);
         if (jobStatus.isPersisted()) {
             maybeWriteStatusToDiskAsync();
@@ -217,7 +216,6 @@
         if (DEBUG) {
             Slog.d(TAG, "Added job status to store: " + jobStatus);
         }
-        return replaced;
     }
 
     /**
diff --git a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
index d7bd030..554f152 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
@@ -41,11 +41,11 @@
      * Called by a {@link com.android.server.job.restrictions.JobRestriction} to notify the
      * JobScheduler that it should check on the state of all jobs.
      *
-     * @param stopLongRunningJobs Whether to stop any jobs that have run for more than their minimum
-     *                            execution guarantee and are restricted by the changed restriction
+     * @param stopOvertimeJobs Whether to stop any jobs that have run for more than their minimum
+     *                         execution guarantee and are restricted by the changed restriction
      */
     void onRestrictionStateChanged(@NonNull JobRestriction restriction,
-            boolean stopLongRunningJobs);
+            boolean stopOvertimeJobs);
 
     /**
      * Called by the controller to notify the JobManager that regardless of the state of the task,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index 65d7121..ecee10a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -81,8 +81,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
     }
 
     @Override
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index d284a99..2ca3f8f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -133,7 +133,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob) {
         if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
             mTrackedTasks.remove(taskStatus);
             mTopStartedJobs.remove(taskStatus);
@@ -143,7 +143,7 @@
     @Override
     public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
         if (!jobStatus.hasPowerConstraint()) {
-            maybeStopTrackingJobLocked(jobStatus, null, false);
+            maybeStopTrackingJobLocked(jobStatus, null);
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
index 9b59560..b029e00 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
@@ -127,8 +127,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
     }
 
     @Override
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index d2dc2a7e..16dd1672 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -290,8 +290,7 @@
 
     @GuardedBy("mLock")
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
         if (jobStatus.clearTrackingController(JobStatus.TRACKING_CONNECTIVITY)) {
             ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid());
             if (jobs != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
index 83a756c..847a1bf 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
@@ -159,8 +159,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob) {
         if (taskStatus.clearTrackingController(JobStatus.TRACKING_CONTENT)) {
             mTrackedTasks.remove(taskStatus);
             if (taskStatus.contentObserverJobInstance != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index abbe177..bdf72b6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -225,8 +225,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
         if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
             mAllowInIdleJobs.remove(jobStatus);
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index 547f94ba..4c17692 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -213,7 +213,7 @@
 
     @Override
     @GuardedBy("mLock")
-    public void maybeStopTrackingJobLocked(JobStatus js, JobStatus incomingJob, boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus js, JobStatus incomingJob) {
         if (js.clearTrackingController(JobStatus.TRACKING_FLEXIBILITY)) {
             mFlexibilityAlarmQueue.removeAlarmForKey(js);
             mFlexibilityTracker.remove(js);
@@ -342,10 +342,10 @@
             // There is no deadline and no estimated launch time.
             return NO_LIFECYCLE_END;
         }
-        if (js.getNumFailures() > 1) {
-            // Number of failures will not equal one as per restriction in JobStatus constructor.
+        // Increase the flex deadline for jobs rescheduled more than once.
+        if (js.getNumPreviousAttempts() > 1) {
             return earliest + Math.min(
-                    (long) Math.scalb(mRescheduledJobDeadline, js.getNumFailures() - 2),
+                    (long) Math.scalb(mRescheduledJobDeadline, js.getNumPreviousAttempts() - 2),
                     mMaxRescheduledDeadline);
         }
         return js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index 926cfc1..a25af71 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -76,8 +76,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob) {
         if (taskStatus.clearTrackingController(JobStatus.TRACKING_IDLE)) {
             mTrackedTasks.remove(taskStatus);
         }
@@ -86,7 +85,7 @@
     @Override
     public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
         if (!jobStatus.hasIdleConstraint()) {
-            maybeStopTrackingJobLocked(jobStatus, null, false);
+            maybeStopTrackingJobLocked(jobStatus, null);
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index de602a8..f9fb0d0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -241,10 +241,22 @@
      */
     private long mOriginalLatestRunTimeElapsedMillis;
 
-    /** How many times this job has failed, used to compute back-off. */
+    /**
+     * How many times this job has failed to complete on its own
+     * (via {@link android.app.job.JobService#jobFinished(JobParameters, boolean)} or because of
+     * a timeout).
+     * This count doesn't include most times JobScheduler decided to stop the job
+     * (via {@link android.app.job.JobService#onStopJob(JobParameters)}.
+     */
     private final int numFailures;
 
     /**
+     * The number of times JobScheduler has forced this job to stop due to reasons mostly outside
+     * of the app's control.
+     */
+    private final int mNumSystemStops;
+
+    /**
      * Which app standby bucket this job's app is in.  Updated when the app is moved to a
      * different bucket.
      */
@@ -488,6 +500,8 @@
      * @param tag A string associated with the job for debugging/logging purposes.
      * @param numFailures Count of how many times this job has requested a reschedule because
      *     its work was not yet finished.
+     * @param numSystemStops Count of how many times JobScheduler has forced this job to stop due to
+     *     factors mostly out of the app's control.
      * @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job
      *     is to be considered runnable
      * @param latestRunTimeElapsedMillis Milestone: point in time at which the job will be
@@ -497,7 +511,7 @@
      * @param internalFlags Non-API property flags about this job
      */
     private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
-            int sourceUserId, int standbyBucket, String tag, int numFailures,
+            int sourceUserId, int standbyBucket, String tag, int numFailures, int numSystemStops,
             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
             long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags,
             int dynamicConstraints) {
@@ -535,6 +549,7 @@
         this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
         this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
         this.numFailures = numFailures;
+        mNumSystemStops = numSystemStops;
 
         int requiredConstraints = job.getConstraintFlags();
         if (job.getRequiredNetwork() != null) {
@@ -576,7 +591,7 @@
         // Otherwise, every consecutive reschedule increases a jobs' flexibility deadline.
         if (!isRequestedExpeditedJob()
                 && satisfiesMinWindowException
-                && numFailures != 1
+                && (numFailures + numSystemStops) != 1
                 && lacksSomeFlexibleConstraints) {
             mNumRequiredFlexibleConstraints =
                     NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mPreferUnmetered ? 1 : 0);
@@ -626,7 +641,7 @@
         this(jobStatus.getJob(), jobStatus.getUid(),
                 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
                 jobStatus.getStandbyBucket(),
-                jobStatus.getSourceTag(), jobStatus.getNumFailures(),
+                jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(),
                 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
                 jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
                 jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints);
@@ -654,7 +669,7 @@
             int innerFlags, int dynamicConstraints) {
         this(job, callingUid, sourcePkgName, sourceUserId,
                 standbyBucket,
-                sourceTag, 0,
+                sourceTag, /* numFailures */ 0, /* numSystemStops */ 0,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
                 lastSuccessfulRunTime, lastFailedRunTime, innerFlags, dynamicConstraints);
 
@@ -673,12 +688,13 @@
     /** Create a new job to be rescheduled with the provided parameters. */
     public JobStatus(JobStatus rescheduling,
             long newEarliestRuntimeElapsedMillis,
-            long newLatestRuntimeElapsedMillis, int backoffAttempt,
+            long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops,
             long lastSuccessfulRunTime, long lastFailedRunTime) {
         this(rescheduling.job, rescheduling.getUid(),
                 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
                 rescheduling.getStandbyBucket(),
-                rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
+                rescheduling.getSourceTag(), numFailures, numSystemStops,
+                newEarliestRuntimeElapsedMillis,
                 newLatestRuntimeElapsedMillis,
                 lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags(),
                 rescheduling.mDynamicConstraints);
@@ -715,7 +731,7 @@
         int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
                 sourceUserId, elapsedNow);
         return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
-                standbyBucket, tag, 0,
+                standbyBucket, tag, /* numFailures */ 0, /* numSystemStops */ 0,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
                 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
                 /*innerFlags=*/ 0, /* dynamicConstraints */ 0);
@@ -868,10 +884,27 @@
         pw.print(job.getId());
     }
 
+    /**
+     * Returns the number of times the job stopped previously for reasons that appeared to be within
+     * the app's control.
+     */
     public int getNumFailures() {
         return numFailures;
     }
 
+    /**
+     * Returns the number of times the system stopped a previous execution of this job for reasons
+     * that were likely outside the app's control.
+     */
+    public int getNumSystemStops() {
+        return mNumSystemStops;
+    }
+
+    /** Returns the total number of times we've attempted to run this job in the past. */
+    public int getNumPreviousAttempts() {
+        return numFailures + mNumSystemStops;
+    }
+
     public ComponentName getServiceComponent() {
         return job.getService();
     }
@@ -1857,6 +1890,10 @@
             sb.append(" failures=");
             sb.append(numFailures);
         }
+        if (mNumSystemStops != 0) {
+            sb.append(" system stops=");
+            sb.append(mNumSystemStops);
+        }
         if (isReady()) {
             sb.append(" READY");
         } else {
@@ -2382,6 +2419,9 @@
         if (numFailures != 0) {
             pw.print("Num failures: "); pw.println(numFailures);
         }
+        if (mNumSystemStops != 0) {
+            pw.print("Num system stops: "); pw.println(mNumSystemStops);
+        }
         if (mLastSuccessfulRunTime != 0) {
             pw.print("Last successful run: ");
             pw.println(formatTime(mLastSuccessfulRunTime));
@@ -2579,7 +2619,7 @@
         proto.write(JobStatusDumpProto.ORIGINAL_LATEST_RUNTIME_ELAPSED,
                 mOriginalLatestRunTimeElapsedMillis);
 
-        proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures);
+        proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures + mNumSystemStops);
         proto.write(JobStatusDumpProto.LAST_SUCCESSFUL_RUN_TIME, mLastSuccessfulRunTime);
         proto.write(JobStatusDumpProto.LAST_FAILED_RUN_TIME, mLastFailedRunTime);
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index e04cec3..d69b9e0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -167,8 +167,7 @@
 
     @Override
     @GuardedBy("mLock")
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
         final int userId = jobStatus.getSourceUserId();
         final String pkgName = jobStatus.getSourcePackageName();
         final ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index bb8d175..659e7c0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -673,8 +673,7 @@
 
     @Override
     @GuardedBy("mLock")
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
         if (jobStatus.clearTrackingController(JobStatus.TRACKING_QUOTA)) {
             unprepareFromExecutionLocked(jobStatus);
             final int userId = jobStatus.getSourceUserId();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index 8453e53..0eedcf0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -85,8 +85,7 @@
     /**
      * Remove task - this will happen if the task is cancelled, completed, etc.
      */
-    public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-            boolean forUpdate);
+    public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob);
 
     /**
      * Called when a new job is being created to reschedule an old failed job.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
index 1ce0a7f6..11e2ff7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
@@ -70,8 +70,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob) {
         if (taskStatus.clearTrackingController(JobStatus.TRACKING_STORAGE)) {
             mTrackedTasks.remove(taskStatus);
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
index b2ca3a0..cafb02d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
@@ -383,8 +383,7 @@
 
     @Override
     @GuardedBy("mLock")
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
         final int userId = jobStatus.getSourceUserId();
         final String pkgName = jobStatus.getSourcePackageName();
         if (!mTopStartedJobs.remove(jobStatus) && jobStatus.madeActive > 0) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index b6361ce..5195f28 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -79,7 +79,7 @@
     @Override
     public void maybeStartTrackingJobLocked(JobStatus job, JobStatus lastJob) {
         if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
-            maybeStopTrackingJobLocked(job, null, false);
+            maybeStopTrackingJobLocked(job, null);
 
             // First: check the constraints now, because if they are already satisfied
             // then there is no need to track it.  This gives us a fast path for a common
@@ -134,8 +134,7 @@
      * tracking was the one our alarms were based off of.
      */
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus job, JobStatus incomingJob,
-            boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus job, JobStatus incomingJob) {
         if (job.clearTrackingController(JobStatus.TRACKING_TIME)) {
             if (mTrackedJobs.remove(job)) {
                 checkExpiredDelaysAndResetAlarm();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index a007a69..ca2fd60 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -90,11 +90,11 @@
         final int priority = job.getEffectivePriority();
         if (mThermalStatus >= HIGHER_PRIORITY_THRESHOLD) {
             // For moderate throttling, only let expedited jobs and high priority regular jobs that
-            // haven't been running for long run.
+            // haven't been running for a long time run.
             return !job.shouldTreatAsExpeditedJob()
                     && !(priority == JobInfo.PRIORITY_HIGH
                         && mService.isCurrentlyRunningLocked(job)
-                        && !mService.isLongRunningLocked(job));
+                        && !mService.isJobInOvertimeLocked(job));
         }
         if (mThermalStatus >= LOW_PRIORITY_THRESHOLD) {
             // For light throttling, throttle all min priority jobs and all low priority jobs that
@@ -102,7 +102,7 @@
             return priority == JobInfo.PRIORITY_MIN
                     || (priority == JobInfo.PRIORITY_LOW
                         && (!mService.isCurrentlyRunningLocked(job)
-                            || mService.isLongRunningLocked(job)));
+                            || mService.isJobInOvertimeLocked(job)));
         }
         return false;
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index b426f16..46338fa 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -33,9 +33,10 @@
 import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES;
-import static android.app.tare.EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES;
@@ -71,9 +72,10 @@
 import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
 import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
 import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
-import static android.app.tare.EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT;
 import static android.app.tare.EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT;
 import static android.app.tare.EconomyManager.KEY_AM_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT;
 import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED;
 import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP;
 import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT;
@@ -146,7 +148,8 @@
     private long mMinSatiatedBalanceOther;
     private long mMaxSatiatedBalance;
     private long mInitialSatiatedConsumptionLimit;
-    private long mHardSatiatedConsumptionLimit;
+    private long mMinSatiatedConsumptionLimit;
+    private long mMaxSatiatedConsumptionLimit;
 
     private final KeyValueListParser mParser = new KeyValueListParser(',');
     private final Injector mInjector;
@@ -199,8 +202,13 @@
     }
 
     @Override
-    long getHardSatiatedConsumptionLimit() {
-        return mHardSatiatedConsumptionLimit;
+    long getMinSatiatedConsumptionLimit() {
+        return mMinSatiatedConsumptionLimit;
+    }
+
+    @Override
+    long getMaxSatiatedConsumptionLimit() {
+        return mMaxSatiatedConsumptionLimit;
     }
 
     @NonNull
@@ -240,12 +248,15 @@
         mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
             KEY_AM_MAX_SATIATED_BALANCE, DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES,
             Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
+        mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+                KEY_AM_MIN_CONSUMPTION_LIMIT, DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES,
+                arcToCake(1));
         mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
-            KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
-            arcToCake(1));
-        mHardSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
-            KEY_AM_HARD_CONSUMPTION_LIMIT, DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES,
-            mInitialSatiatedConsumptionLimit);
+                KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
+                mMinSatiatedConsumptionLimit);
+        mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+                KEY_AM_MAX_CONSUMPTION_LIMIT, DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES,
+                mInitialSatiatedConsumptionLimit);
 
         final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(mParser, properties,
                 KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
@@ -396,9 +407,11 @@
         pw.decreaseIndent();
         pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println();
         pw.print("Consumption limits: [");
+        pw.print(cakeToString(mMinSatiatedConsumptionLimit));
+        pw.print(", ");
         pw.print(cakeToString(mInitialSatiatedConsumptionLimit));
         pw.print(", ");
-        pw.print(cakeToString(mHardSatiatedConsumptionLimit));
+        pw.print(cakeToString(mMaxSatiatedConsumptionLimit));
         pw.println("]");
 
         pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
index 66f7c35..7a96076 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -43,7 +43,8 @@
     private int mEnabledEconomicPolicyIds = 0;
     private int[] mCostModifiers = EmptyArray.INT;
     private long mInitialConsumptionLimit;
-    private long mHardConsumptionLimit;
+    private long mMinConsumptionLimit;
+    private long mMaxConsumptionLimit;
 
     CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
         this(irs, new CompleteInjector());
@@ -100,14 +101,17 @@
 
     private void updateLimits() {
         long initialConsumptionLimit = 0;
-        long hardConsumptionLimit = 0;
+        long minConsumptionLimit = 0;
+        long maxConsumptionLimit = 0;
         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
             final EconomicPolicy economicPolicy = mEnabledEconomicPolicies.valueAt(i);
             initialConsumptionLimit += economicPolicy.getInitialSatiatedConsumptionLimit();
-            hardConsumptionLimit += economicPolicy.getHardSatiatedConsumptionLimit();
+            minConsumptionLimit += economicPolicy.getMinSatiatedConsumptionLimit();
+            maxConsumptionLimit += economicPolicy.getMaxSatiatedConsumptionLimit();
         }
         mInitialConsumptionLimit = initialConsumptionLimit;
-        mHardConsumptionLimit = hardConsumptionLimit;
+        mMinConsumptionLimit = minConsumptionLimit;
+        mMaxConsumptionLimit = maxConsumptionLimit;
     }
 
     @Override
@@ -134,8 +138,13 @@
     }
 
     @Override
-    long getHardSatiatedConsumptionLimit() {
-        return mHardConsumptionLimit;
+    long getMinSatiatedConsumptionLimit() {
+        return mMinConsumptionLimit;
+    }
+
+    @Override
+    long getMaxSatiatedConsumptionLimit() {
+        return mMaxConsumptionLimit;
     }
 
     @NonNull
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index 008dcb8..b52f6f1 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -232,15 +232,21 @@
      * Returns the maximum number of cakes that should be consumed during a full 100% discharge
      * cycle. This is the initial limit. The system may choose to increase the limit over time,
      * but the increased limit should never exceed the value returned from
-     * {@link #getHardSatiatedConsumptionLimit()}.
+     * {@link #getMaxSatiatedConsumptionLimit()}.
      */
     abstract long getInitialSatiatedConsumptionLimit();
 
     /**
-     * Returns the maximum number of cakes that should be consumed during a full 100% discharge
-     * cycle. This is the hard limit that should never be exceeded.
+     * Returns the minimum number of cakes that should be available for consumption during a full
+     * 100% discharge cycle.
      */
-    abstract long getHardSatiatedConsumptionLimit();
+    abstract long getMinSatiatedConsumptionLimit();
+
+    /**
+     * Returns the maximum number of cakes that should be available for consumption during a full
+     * 100% discharge cycle.
+     */
+    abstract long getMaxSatiatedConsumptionLimit();
 
     /** Return the set of modifiers that should apply to this policy's costs. */
     @NonNull
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index dd0a194..581a545 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -670,7 +670,7 @@
         final long shortfall = (mCurrentBatteryLevel - QUANTITATIVE_EASING_BATTERY_THRESHOLD)
                 * currentConsumptionLimit / 100;
         final long newConsumptionLimit = Math.min(currentConsumptionLimit + shortfall,
-                mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit());
+                mCompleteEconomicPolicy.getMaxSatiatedConsumptionLimit());
         if (newConsumptionLimit != currentConsumptionLimit) {
             Slog.i(TAG, "Increasing consumption limit from " + cakeToString(currentConsumptionLimit)
                     + " to " + cakeToString(newConsumptionLimit));
@@ -720,12 +720,12 @@
             // The stock is too low. We're doing pretty well. We can increase the stock slightly
             // to let apps do more work in the background.
             newConsumptionLimit = Math.min((long) (currentConsumptionLimit * 1.01),
-                    mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit());
+                    mCompleteEconomicPolicy.getMaxSatiatedConsumptionLimit());
         } else if (percentageOfTarget < 100) {
             // The stock is too high IMO. We're below the target. Decrease the stock to reduce
             // background work.
             newConsumptionLimit = Math.max((long) (currentConsumptionLimit * .98),
-                    mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
+                    mCompleteEconomicPolicy.getMinSatiatedConsumptionLimit());
         } else {
             // The stock is just right.
             return;
@@ -957,9 +957,9 @@
             } else {
                 mScribe.loadFromDiskLocked();
                 if (mScribe.getSatiatedConsumptionLimitLocked()
-                        < mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()
+                        < mCompleteEconomicPolicy.getMinSatiatedConsumptionLimit()
                         || mScribe.getSatiatedConsumptionLimitLocked()
-                        > mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) {
+                        > mCompleteEconomicPolicy.getMaxSatiatedConsumptionLimit()) {
                     // Reset the consumption limit since several factors may have changed.
                     mScribe.setConsumptionLimitLocked(
                             mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
@@ -1442,17 +1442,16 @@
 
         private void updateEconomicPolicy() {
             synchronized (mLock) {
-                final long initialLimit =
-                        mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
-                final long hardLimit = mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit();
+                final long minLimit = mCompleteEconomicPolicy.getMinSatiatedConsumptionLimit();
+                final long maxLimit = mCompleteEconomicPolicy.getMaxSatiatedConsumptionLimit();
                 final int oldEnabledPolicies = mCompleteEconomicPolicy.getEnabledPolicyIds();
                 mCompleteEconomicPolicy.tearDown();
                 mCompleteEconomicPolicy = new CompleteEconomicPolicy(InternalResourceService.this);
                 if (mIsEnabled && mBootPhase >= PHASE_THIRD_PARTY_APPS_CAN_START) {
                     mCompleteEconomicPolicy.setup(getAllDeviceConfigProperties());
-                    if (initialLimit != mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()
-                            || hardLimit
-                            != mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) {
+                    if (minLimit != mCompleteEconomicPolicy.getMinSatiatedConsumptionLimit()
+                            || maxLimit
+                            != mCompleteEconomicPolicy.getMaxSatiatedConsumptionLimit()) {
                         // Reset the consumption limit since several factors may have changed.
                         mScribe.setConsumptionLimitLocked(
                                 mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 71c6d09..7cf459c 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -38,9 +38,10 @@
 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES;
-import static android.app.tare.EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES;
@@ -84,9 +85,10 @@
 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_CTP;
 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
-import static android.app.tare.EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT;
 import static android.app.tare.EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT;
 import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT;
 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER;
 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -159,7 +161,8 @@
     private long mMinSatiatedBalanceIncrementalAppUpdater;
     private long mMaxSatiatedBalance;
     private long mInitialSatiatedConsumptionLimit;
-    private long mHardSatiatedConsumptionLimit;
+    private long mMinSatiatedConsumptionLimit;
+    private long mMaxSatiatedConsumptionLimit;
 
     private final KeyValueListParser mParser = new KeyValueListParser(',');
     private final Injector mInjector;
@@ -216,8 +219,13 @@
     }
 
     @Override
-    long getHardSatiatedConsumptionLimit() {
-        return mHardSatiatedConsumptionLimit;
+    long getMinSatiatedConsumptionLimit() {
+        return mMinSatiatedConsumptionLimit;
+    }
+
+    @Override
+    long getMaxSatiatedConsumptionLimit() {
+        return mMaxSatiatedConsumptionLimit;
     }
 
     @NonNull
@@ -260,12 +268,15 @@
         mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
             KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
             Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
+        mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+                KEY_JS_MIN_CONSUMPTION_LIMIT, DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES,
+                arcToCake(1));
         mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
-            KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
-            arcToCake(1));
-        mHardSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
-            KEY_JS_HARD_CONSUMPTION_LIMIT, DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES,
-            mInitialSatiatedConsumptionLimit);
+                KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
+                mMinSatiatedConsumptionLimit);
+        mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+                KEY_JS_MAX_CONSUMPTION_LIMIT, DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES,
+                mInitialSatiatedConsumptionLimit);
 
         mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
                 getConstantAsCake(mParser, properties,
@@ -420,9 +431,11 @@
         pw.decreaseIndent();
         pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println();
         pw.print("Consumption limits: [");
+        pw.print(cakeToString(mMinSatiatedConsumptionLimit));
+        pw.print(", ");
         pw.print(cakeToString(mInitialSatiatedConsumptionLimit));
         pw.print(", ");
-        pw.print(cakeToString(mHardSatiatedConsumptionLimit));
+        pw.print(cakeToString(mMaxSatiatedConsumptionLimit));
         pw.println("]");
 
         pw.println();
diff --git a/core/api/current.txt b/core/api/current.txt
index 40e0c5b..b42face 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -172,6 +172,7 @@
     field public static final String REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE = "android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE";
     field public static final String REQUEST_PASSWORD_COMPLEXITY = "android.permission.REQUEST_PASSWORD_COMPLEXITY";
     field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+    field public static final String RUN_LONG_JOBS = "android.permission.RUN_LONG_JOBS";
     field public static final String SCHEDULE_EXACT_ALARM = "android.permission.SCHEDULE_EXACT_ALARM";
     field public static final String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
     field public static final String SEND_SMS = "android.permission.SEND_SMS";
@@ -4603,23 +4604,24 @@
 
   public class AlarmManager {
     method public boolean canScheduleExactAlarms();
-    method public void cancel(android.app.PendingIntent);
-    method public void cancel(android.app.AlarmManager.OnAlarmListener);
+    method public void cancel(@NonNull android.app.PendingIntent);
+    method public void cancel(@NonNull android.app.AlarmManager.OnAlarmListener);
     method public void cancelAll();
     method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock();
-    method public void set(int, long, android.app.PendingIntent);
-    method public void set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
-    method @RequiresPermission(android.Manifest.permission.SCHEDULE_EXACT_ALARM) public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
-    method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent);
-    method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, android.app.PendingIntent);
-    method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
-    method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
-    method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
-    method public void setRepeating(int, long, long, android.app.PendingIntent);
+    method public void set(int, long, @NonNull android.app.PendingIntent);
+    method public void set(int, long, @Nullable String, @NonNull android.app.AlarmManager.OnAlarmListener, @Nullable android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.SCHEDULE_EXACT_ALARM) public void setAlarmClock(@NonNull android.app.AlarmManager.AlarmClockInfo, @NonNull android.app.PendingIntent);
+    method public void setAndAllowWhileIdle(int, long, @NonNull android.app.PendingIntent);
+    method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, @NonNull android.app.PendingIntent);
+    method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, @Nullable String, @NonNull android.app.AlarmManager.OnAlarmListener, @Nullable android.os.Handler);
+    method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExactAndAllowWhileIdle(int, long, @NonNull android.app.PendingIntent);
+    method public void setInexactRepeating(int, long, long, @NonNull android.app.PendingIntent);
+    method public void setRepeating(int, long, long, @NonNull android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.SET_TIME) public void setTime(long);
     method @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE) public void setTimeZone(String);
-    method public void setWindow(int, long, long, android.app.PendingIntent);
-    method public void setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
+    method public void setWindow(int, long, long, @NonNull android.app.PendingIntent);
+    method public void setWindow(int, long, long, @Nullable String, @NonNull android.app.AlarmManager.OnAlarmListener, @Nullable android.os.Handler);
+    method public void setWindow(int, long, long, @Nullable String, @NonNull java.util.concurrent.Executor, @NonNull android.app.AlarmManager.OnAlarmListener);
     field public static final String ACTION_NEXT_ALARM_CLOCK_CHANGED = "android.app.action.NEXT_ALARM_CLOCK_CHANGED";
     field public static final String ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED = "android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED";
     field public static final int ELAPSED_REALTIME = 3; // 0x3
@@ -7456,6 +7458,7 @@
     method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
     method @NonNull public java.util.List<java.lang.String> getMeteredDataDisabledPackages(@NonNull android.content.ComponentName);
     method public int getMinimumRequiredWifiSecurityLevel();
+    method public int getMtePolicy();
     method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyAppStreamingPolicy();
     method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyNotificationStreamingPolicy();
     method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName);
@@ -7604,6 +7607,7 @@
     method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long);
     method @NonNull public java.util.List<java.lang.String> setMeteredDataDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
     method public void setMinimumRequiredWifiSecurityLevel(int);
+    method public void setMtePolicy(int);
     method public void setNearbyAppStreamingPolicy(int);
     method public void setNearbyNotificationStreamingPolicy(int);
     method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean);
@@ -7787,6 +7791,9 @@
     field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
     field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
     field public static final String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
+    field public static final int MTE_DISABLED = 2; // 0x2
+    field public static final int MTE_ENABLED = 1; // 0x1
+    field public static final int MTE_NOT_CONTROLLED_BY_POLICY = 0; // 0x0
     field public static final int NEARBY_STREAMING_DISABLED = 1; // 0x1
     field public static final int NEARBY_STREAMING_ENABLED = 2; // 0x2
     field public static final int NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY = 0; // 0x0
@@ -11648,7 +11655,7 @@
   public static final class PackageInstaller.PreapprovalDetails implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.graphics.Bitmap getIcon();
-    method @NonNull public String getLabel();
+    method @NonNull public CharSequence getLabel();
     method @NonNull public android.icu.util.ULocale getLocale();
     method @NonNull public String getPackageName();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -11659,7 +11666,7 @@
     ctor public PackageInstaller.PreapprovalDetails.Builder();
     method @NonNull public android.content.pm.PackageInstaller.PreapprovalDetails build();
     method @NonNull public android.content.pm.PackageInstaller.PreapprovalDetails.Builder setIcon(@NonNull android.graphics.Bitmap);
-    method @NonNull public android.content.pm.PackageInstaller.PreapprovalDetails.Builder setLabel(@NonNull String);
+    method @NonNull public android.content.pm.PackageInstaller.PreapprovalDetails.Builder setLabel(@NonNull CharSequence);
     method @NonNull public android.content.pm.PackageInstaller.PreapprovalDetails.Builder setLocale(@NonNull android.icu.util.ULocale);
     method @NonNull public android.content.pm.PackageInstaller.PreapprovalDetails.Builder setPackageName(@NonNull String);
   }
@@ -19497,7 +19504,7 @@
     method public boolean hasSatelliteBlocklist();
     method public boolean hasSatellitePvt();
     method public boolean hasScheduling();
-    method public boolean hasSingleShot();
+    method public boolean hasSingleShotFix();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssCapabilities> CREATOR;
   }
@@ -19529,7 +19536,7 @@
     method @NonNull public android.location.GnssCapabilities.Builder setHasSatelliteBlocklist(boolean);
     method @NonNull public android.location.GnssCapabilities.Builder setHasSatellitePvt(boolean);
     method @NonNull public android.location.GnssCapabilities.Builder setHasScheduling(boolean);
-    method @NonNull public android.location.GnssCapabilities.Builder setHasSingleShot(boolean);
+    method @NonNull public android.location.GnssCapabilities.Builder setHasSingleShotFix(boolean);
   }
 
   public final class GnssClock implements android.os.Parcelable {
@@ -19727,7 +19734,7 @@
     method public int getConstellationType(@IntRange(from=0) int);
     method @FloatRange(from=0xffffffa6, to=90) public float getElevationDegrees(@IntRange(from=0) int);
     method @IntRange(from=0) public int getSatelliteCount();
-    method @IntRange(from=1, to=200) public int getSvid(@IntRange(from=0) int);
+    method @IntRange(from=1, to=206) public int getSvid(@IntRange(from=0) int);
     method public boolean hasAlmanacData(@IntRange(from=0) int);
     method public boolean hasBasebandCn0DbHz(@IntRange(from=0) int);
     method public boolean hasCarrierFrequencyHz(@IntRange(from=0) int);
@@ -32539,6 +32546,7 @@
     field public static final String DISALLOW_BLUETOOTH = "no_bluetooth";
     field public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing";
     field public static final String DISALLOW_CAMERA_TOGGLE = "disallow_camera_toggle";
+    field public static final String DISALLOW_CELLULAR_2G = "no_cellular_2g";
     field public static final String DISALLOW_CHANGE_WIFI_STATE = "no_change_wifi_state";
     field public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
     field public static final String DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness";
@@ -41585,7 +41593,7 @@
     field public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool";
     field public static final String KEY_CARRIER_CONFIG_VERSION_STRING = "carrier_config_version_string";
     field public static final String KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL = "carrier_cross_sim_ims_available_bool";
-    field public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = "carrier_data_call_permanent_failure_strings";
+    field @Deprecated public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = "carrier_data_call_permanent_failure_strings";
     field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY = "carrier_default_actions_on_dcfailure_string_array";
     field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE = "carrier_default_actions_on_default_network_available_string_array";
     field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY = "carrier_default_actions_on_redirection_string_array";
@@ -41722,6 +41730,7 @@
     field public static final String KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL = "enableMMSReadReports";
     field public static final String KEY_MMS_MULTIPART_SMS_ENABLED_BOOL = "enableMultipartSMS";
     field public static final String KEY_MMS_NAI_SUFFIX_STRING = "naiSuffix";
+    field public static final String KEY_MMS_NETWORK_RELEASE_TIMEOUT_MILLIS_INT = "mms_network_release_timeout_millis_int";
     field public static final String KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL = "enabledNotifyWapMMSC";
     field public static final String KEY_MMS_RECIPIENT_LIMIT_INT = "recipientLimit";
     field public static final String KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL = "sendMultipartSmsAsSeparateMessages";
@@ -43410,14 +43419,18 @@
     field public static final int RESULT_RECEIVE_WHILE_ENCRYPTED = 504; // 0x1f8
     field public static final int RESULT_REMOTE_EXCEPTION = 31; // 0x1f
     field public static final int RESULT_REQUEST_NOT_SUPPORTED = 24; // 0x18
+    field public static final int RESULT_RIL_ABORTED = 137; // 0x89
     field public static final int RESULT_RIL_ACCESS_BARRED = 122; // 0x7a
     field public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123; // 0x7b
     field public static final int RESULT_RIL_CANCELLED = 119; // 0x77
+    field public static final int RESULT_RIL_DEVICE_IN_USE = 136; // 0x88
     field public static final int RESULT_RIL_ENCODING_ERR = 109; // 0x6d
     field public static final int RESULT_RIL_GENERIC_ERROR = 124; // 0x7c
     field public static final int RESULT_RIL_INTERNAL_ERR = 113; // 0x71
     field public static final int RESULT_RIL_INVALID_ARGUMENTS = 104; // 0x68
     field public static final int RESULT_RIL_INVALID_MODEM_STATE = 115; // 0x73
+    field public static final int RESULT_RIL_INVALID_RESPONSE = 125; // 0x7d
+    field public static final int RESULT_RIL_INVALID_SIM_STATE = 130; // 0x82
     field public static final int RESULT_RIL_INVALID_SMSC_ADDRESS = 110; // 0x6e
     field public static final int RESULT_RIL_INVALID_SMS_FORMAT = 107; // 0x6b
     field public static final int RESULT_RIL_INVALID_STATE = 103; // 0x67
@@ -43426,14 +43439,23 @@
     field public static final int RESULT_RIL_NETWORK_NOT_READY = 116; // 0x74
     field public static final int RESULT_RIL_NETWORK_REJECT = 102; // 0x66
     field public static final int RESULT_RIL_NO_MEMORY = 105; // 0x69
+    field public static final int RESULT_RIL_NO_NETWORK_FOUND = 135; // 0x87
     field public static final int RESULT_RIL_NO_RESOURCES = 118; // 0x76
+    field public static final int RESULT_RIL_NO_SMS_TO_ACK = 131; // 0x83
+    field public static final int RESULT_RIL_NO_SUBSCRIPTION = 134; // 0x86
     field public static final int RESULT_RIL_OPERATION_NOT_ALLOWED = 117; // 0x75
     field public static final int RESULT_RIL_RADIO_NOT_AVAILABLE = 100; // 0x64
     field public static final int RESULT_RIL_REQUEST_NOT_SUPPORTED = 114; // 0x72
     field public static final int RESULT_RIL_REQUEST_RATE_LIMITED = 106; // 0x6a
     field public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121; // 0x79
     field public static final int RESULT_RIL_SIM_ABSENT = 120; // 0x78
+    field public static final int RESULT_RIL_SIM_BUSY = 132; // 0x84
+    field public static final int RESULT_RIL_SIM_ERROR = 129; // 0x81
+    field public static final int RESULT_RIL_SIM_FULL = 133; // 0x85
+    field public static final int RESULT_RIL_SIM_PIN2 = 126; // 0x7e
+    field public static final int RESULT_RIL_SIM_PUK2 = 127; // 0x7f
     field public static final int RESULT_RIL_SMS_SEND_FAIL_RETRY = 101; // 0x65
+    field public static final int RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE = 128; // 0x80
     field public static final int RESULT_RIL_SYSTEM_ERR = 108; // 0x6c
     field public static final int RESULT_SMS_BLOCKED_DURING_EMERGENCY = 29; // 0x1d
     field public static final int RESULT_SMS_SEND_RETRY_FAILED = 30; // 0x1e
@@ -44006,9 +44028,10 @@
     field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L
     field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L
     field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L
+    field public static final long NETWORK_TYPE_BITMASK_IDEN = 1024L; // 0x400L
     field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L
     field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L
-    field public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
+    field @Deprecated public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
     field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L
     field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
     field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
@@ -45561,6 +45584,14 @@
     field public static final int DONE = -1; // 0xffffffff
   }
 
+  public static class SegmentFinder.DefaultSegmentFinder extends android.text.SegmentFinder {
+    ctor public SegmentFinder.DefaultSegmentFinder(@NonNull int[]);
+    method public int nextEndBoundary(@IntRange(from=0) int);
+    method public int nextStartBoundary(@IntRange(from=0) int);
+    method public int previousEndBoundary(@IntRange(from=0) int);
+    method public int previousStartBoundary(@IntRange(from=0) int);
+  }
+
   public class Selection {
     method public static boolean extendDown(android.text.Spannable, android.text.Layout);
     method public static boolean extendLeft(android.text.Spannable, android.text.Layout);
@@ -47431,6 +47462,7 @@
     field public static final int DENSITY_420 = 420; // 0x1a4
     field public static final int DENSITY_440 = 440; // 0x1b8
     field public static final int DENSITY_450 = 450; // 0x1c2
+    field public static final int DENSITY_520 = 520; // 0x208
     field public static final int DENSITY_560 = 560; // 0x230
     field public static final int DENSITY_600 = 600; // 0x258
     field public static final int DENSITY_DEFAULT = 160; // 0xa0
@@ -48549,6 +48581,12 @@
     field public static final int VERTICAL_GRAVITY_MASK = 112; // 0x70
   }
 
+  public class HandwritingDelegateConfiguration {
+    ctor public HandwritingDelegateConfiguration(@IdRes int, @NonNull Runnable);
+    method public int getDelegatorViewId();
+    method @NonNull public Runnable getInitiationCallback();
+  }
+
   public class HapticFeedbackConstants {
     field public static final int CLOCK_TICK = 4; // 0x4
     field public static final int CONFIRM = 16; // 0x10
@@ -49566,16 +49604,13 @@
   }
 
   public final class PixelCopy {
-    method @NonNull public static android.view.PixelCopy.Request ofSurface(@NonNull android.view.Surface, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
-    method @NonNull public static android.view.PixelCopy.Request ofSurface(@NonNull android.view.SurfaceView, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
-    method @NonNull public static android.view.PixelCopy.Request ofWindow(@NonNull android.view.Window, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
-    method @NonNull public static android.view.PixelCopy.Request ofWindow(@NonNull android.view.View, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
     method public static void request(@NonNull android.view.SurfaceView, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
     method public static void request(@NonNull android.view.SurfaceView, @Nullable android.graphics.Rect, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
     method public static void request(@NonNull android.view.Surface, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
     method public static void request(@NonNull android.view.Surface, @Nullable android.graphics.Rect, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
     method public static void request(@NonNull android.view.Window, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
     method public static void request(@NonNull android.view.Window, @Nullable android.graphics.Rect, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
+    method public static void request(@NonNull android.view.PixelCopy.Request);
     field public static final int ERROR_DESTINATION_INVALID = 5; // 0x5
     field public static final int ERROR_SOURCE_INVALID = 4; // 0x4
     field public static final int ERROR_SOURCE_NO_DATA = 3; // 0x3
@@ -49584,19 +49619,28 @@
     field public static final int SUCCESS = 0; // 0x0
   }
 
-  public static final class PixelCopy.CopyResult {
-    method @NonNull public android.graphics.Bitmap getBitmap();
-    method public int getStatus();
-  }
-
   public static interface PixelCopy.OnPixelCopyFinishedListener {
     method public void onPixelCopyFinished(int);
   }
 
   public static final class PixelCopy.Request {
-    method public void request();
-    method @NonNull public android.view.PixelCopy.Request setDestinationBitmap(@Nullable android.graphics.Bitmap);
-    method @NonNull public android.view.PixelCopy.Request setSourceRect(@Nullable android.graphics.Rect);
+    method @Nullable public android.graphics.Bitmap getDestinationBitmap();
+    method @Nullable public android.graphics.Rect getSourceRect();
+    method @NonNull public static android.view.PixelCopy.Request.Builder ofSurface(@NonNull android.view.Surface, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.Result>);
+    method @NonNull public static android.view.PixelCopy.Request.Builder ofSurface(@NonNull android.view.SurfaceView, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.Result>);
+    method @NonNull public static android.view.PixelCopy.Request.Builder ofWindow(@NonNull android.view.Window, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.Result>);
+    method @NonNull public static android.view.PixelCopy.Request.Builder ofWindow(@NonNull android.view.View, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.Result>);
+  }
+
+  public static final class PixelCopy.Request.Builder {
+    method @NonNull public android.view.PixelCopy.Request build();
+    method @NonNull public android.view.PixelCopy.Request.Builder setDestinationBitmap(@Nullable android.graphics.Bitmap);
+    method @NonNull public android.view.PixelCopy.Request.Builder setSourceRect(@Nullable android.graphics.Rect);
+  }
+
+  public static final class PixelCopy.Result {
+    method @NonNull public android.graphics.Bitmap getBitmap();
+    method public int getStatus();
   }
 
   public final class PointerIcon implements android.os.Parcelable {
@@ -50159,6 +50203,7 @@
     method public float getHandwritingBoundsOffsetLeft();
     method public float getHandwritingBoundsOffsetRight();
     method public float getHandwritingBoundsOffsetTop();
+    method @Nullable public android.view.HandwritingDelegateConfiguration getHandwritingDelegateConfiguration();
     method public final boolean getHasOverlappingRendering();
     method public final int getHeight();
     method public void getHitRect(android.graphics.Rect);
@@ -50525,6 +50570,7 @@
     method public void setForegroundTintList(@Nullable android.content.res.ColorStateList);
     method public void setForegroundTintMode(@Nullable android.graphics.PorterDuff.Mode);
     method public void setHandwritingBoundsOffsets(float, float, float, float);
+    method public void setHandwritingDelegateConfiguration(@Nullable android.view.HandwritingDelegateConfiguration);
     method public void setHapticFeedbackEnabled(boolean);
     method public void setHasTransientState(boolean);
     method public void setHorizontalFadingEdgeEnabled(boolean);
@@ -53567,6 +53613,7 @@
     method public boolean reportFullscreenMode(boolean);
     method public boolean requestCursorUpdates(int);
     method public default boolean requestCursorUpdates(int, int);
+    method public default void requestTextBoundsInfo(@NonNull android.graphics.RectF, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.inputmethod.TextBoundsInfoResult>);
     method public boolean sendKeyEvent(android.view.KeyEvent);
     method public boolean setComposingRegion(int, int);
     method public default boolean setComposingRegion(int, int, @Nullable android.view.inputmethod.TextAttribute);
@@ -53896,6 +53943,50 @@
     method @NonNull public android.view.inputmethod.TextAttribute.Builder setTextConversionSuggestions(@NonNull java.util.List<java.lang.String>);
   }
 
+  public final class TextBoundsInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @IntRange(from=0, to=125) public int getCharacterBidiLevel(int);
+    method @NonNull public android.graphics.RectF getCharacterBounds(int);
+    method public int getCharacterFlags(int);
+    method public int getEnd();
+    method @NonNull public android.text.SegmentFinder getGraphemeSegmentFinder();
+    method @NonNull public android.text.SegmentFinder getLineSegmentFinder();
+    method @NonNull public android.graphics.Matrix getMatrix();
+    method public int getStart();
+    method @NonNull public android.text.SegmentFinder getWordSegmentFinder();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.TextBoundsInfo> CREATOR;
+    field public static final int FLAG_CHARACTER_LINEFEED = 2; // 0x2
+    field public static final int FLAG_CHARACTER_PUNCTUATION = 4; // 0x4
+    field public static final int FLAG_CHARACTER_WHITESPACE = 1; // 0x1
+    field public static final int FLAG_LINE_IS_RTL = 8; // 0x8
+  }
+
+  public static final class TextBoundsInfo.Builder {
+    ctor public TextBoundsInfo.Builder();
+    method @NonNull public android.view.inputmethod.TextBoundsInfo build();
+    method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder clear();
+    method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder setCharacterBidiLevel(@NonNull int[]);
+    method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder setCharacterBounds(@NonNull float[]);
+    method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder setCharacterFlags(@NonNull int[]);
+    method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder setGraphemeSegmentFinder(@NonNull android.text.SegmentFinder);
+    method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder setLineSegmentFinder(@NonNull android.text.SegmentFinder);
+    method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder setMatrix(@NonNull android.graphics.Matrix);
+    method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder setStartAndEnd(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder setWordSegmentFinder(@NonNull android.text.SegmentFinder);
+  }
+
+  public final class TextBoundsInfoResult {
+    ctor public TextBoundsInfoResult(int);
+    ctor public TextBoundsInfoResult(int, @NonNull android.view.inputmethod.TextBoundsInfo);
+    method public int getResultCode();
+    method @Nullable public android.view.inputmethod.TextBoundsInfo getTextBoundsInfo();
+    field public static final int CODE_CANCELLED = 3; // 0x3
+    field public static final int CODE_FAILED = 2; // 0x2
+    field public static final int CODE_SUCCESS = 1; // 0x1
+    field public static final int CODE_UNSUPPORTED = 0; // 0x0
+  }
+
   public final class TextSnapshot {
     ctor public TextSnapshot(@NonNull android.view.inputmethod.SurroundingText, @IntRange(from=0xffffffff) int, @IntRange(from=0xffffffff) int, int);
     method @IntRange(from=0xffffffff) public int getCompositionEnd();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index e890005..ce18745 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -301,10 +301,6 @@
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void reportNetworkInterfaceForTransports(@NonNull String, @NonNull int[]) throws java.lang.RuntimeException;
   }
 
-  public class Binder implements android.os.IBinder {
-    method public final void markVintfStability();
-  }
-
   public class BluetoothServiceManager {
     method @NonNull public android.os.BluetoothServiceManager.ServiceRegisterer getBluetoothManagerServiceRegisterer();
   }
@@ -344,10 +340,6 @@
     method public boolean shouldBypassCache(@NonNull Q);
   }
 
-  public interface Parcelable {
-    method public default int getStability();
-  }
-
   public class Process {
     method public static final int getAppUidForSdkSandboxUid(int);
     method public static final boolean isSdkSandboxUid(int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 74288ae..dcbe7ed 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -524,10 +524,12 @@
   }
 
   public class AlarmManager {
-    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.PendingIntent, android.os.WorkSource);
-    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource);
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, @NonNull android.app.PendingIntent, @Nullable android.os.WorkSource);
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, @NonNull android.app.AlarmManager.OnAlarmListener, @Nullable android.os.Handler, @Nullable android.os.WorkSource);
     method @RequiresPermission(allOf={android.Manifest.permission.UPDATE_DEVICE_STATS, android.Manifest.permission.SCHEDULE_EXACT_ALARM}, conditional=true) public void setExact(int, long, @Nullable String, @NonNull java.util.concurrent.Executor, @NonNull android.os.WorkSource, @NonNull android.app.AlarmManager.OnAlarmListener);
+    method @RequiresPermission(allOf={android.Manifest.permission.UPDATE_DEVICE_STATS, android.Manifest.permission.SCHEDULE_EXACT_ALARM}, conditional=true) public void setExactAndAllowWhileIdle(int, long, @Nullable String, @NonNull java.util.concurrent.Executor, @Nullable android.os.WorkSource, @NonNull android.app.AlarmManager.OnAlarmListener);
     method @RequiresPermission(android.Manifest.permission.SCHEDULE_PRIORITIZED_ALARM) public void setPrioritized(int, long, long, @Nullable String, @NonNull java.util.concurrent.Executor, @NonNull android.app.AlarmManager.OnAlarmListener);
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void setWindow(int, long, long, @Nullable String, @NonNull java.util.concurrent.Executor, @Nullable android.os.WorkSource, @NonNull android.app.AlarmManager.OnAlarmListener);
   }
 
   public class AppOpsManager {
@@ -9351,6 +9353,7 @@
 
   public class Binder implements android.os.IBinder {
     method public int handleShellCommand(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.ParcelFileDescriptor, @NonNull android.os.ParcelFileDescriptor, @NonNull String[]);
+    method public final void markVintfStability();
     method public static void setProxyTransactListener(@Nullable android.os.Binder.ProxyTransactListener);
   }
 
@@ -9669,6 +9672,7 @@
   }
 
   public interface Parcelable {
+    method public default int getStability();
     field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0
     field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1
   }
@@ -9677,7 +9681,6 @@
     ctor public ParcelableHolder(int);
     method public int describeContents();
     method @Nullable public <T extends android.os.Parcelable> T getParcelable(@NonNull Class<T>);
-    method public int getStability();
     method public void readFromParcel(@NonNull android.os.Parcel);
     method public void setParcelable(@Nullable android.os.Parcelable);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -13397,7 +13400,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
     method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) public android.os.UserHandle getUserHandle(int);
+    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) public android.os.UserHandle getSubscriptionUserHandle(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
     method public void requestEmbeddedSubscriptionInfoListRefresh();
     method public void requestEmbeddedSubscriptionInfoListRefresh(int);
@@ -13407,8 +13410,8 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) public void setSubscriptionUserHandle(int, @Nullable android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) public void setUserHandle(int, @Nullable android.os.UserHandle);
     field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
     field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
     field @NonNull public static final android.net.Uri CROSS_SIM_ENABLED_CONTENT_URI;
@@ -13703,6 +13706,7 @@
     field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
     field public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3; // 0x3
     field public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 1; // 0x1
+    field public static final int ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS = 4; // 0x4
     field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2
     field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
     field public static final int CALL_WAITING_STATUS_FDN_CHECK_FAILURE = 5; // 0x5
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ef74a3e..6e87134 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -419,7 +419,7 @@
   }
 
   public final class SyncNotedAppOp implements android.os.Parcelable {
-    ctor public SyncNotedAppOp(int, @IntRange(from=0L) int, @Nullable String, @NonNull String);
+    ctor public SyncNotedAppOp(int, @IntRange(from=0L) int, @Nullable String, @Nullable String);
   }
 
   public class TaskInfo {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 4b1b0a2..baa3bd9 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -227,6 +227,12 @@
     public abstract boolean isModernQueueEnabled();
 
     /**
+     * Enforce capability restrictions on use of the given BroadcastOptions
+     */
+    public abstract void enforceBroadcastOptionsPermissions(@Nullable Bundle options,
+            int callingUid);
+
+    /**
      * Returns package name given pid.
      *
      * @param pid The pid we are searching package name for.
@@ -300,7 +306,7 @@
     public abstract int handleIncomingUser(int callingPid, int callingUid, @UserIdInt int userId,
             boolean allowAll, int allowMode, String name, String callerPackage);
 
-    /** Checks if the calling binder pid as the permission. */
+    /** Checks if the calling binder pid/uid has the given permission. */
     @PermissionMethod
     public abstract void enforceCallingPermission(@PermissionName String permission, String func);
 
@@ -389,6 +395,10 @@
      */
     public abstract boolean isAppStartModeDisabled(int uid, String packageName);
 
+    /**
+     * Returns the ids of the current user and all of its profiles (if any), regardless of the
+     * running state of the profiles.
+     */
     public abstract int[] getCurrentProfileIds();
     public abstract UserInfo getCurrentUser();
     public abstract void ensureNotSpecialUser(@UserIdInt int userId);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d1275f6..1b972e0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1353,9 +1353,16 @@
     public static final int OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO =
             AppProtoEnums.APP_OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
 
+    /**
+     * App can schedule long running jobs.
+     *
+     * @hide
+     */
+    public static final int OP_RUN_LONG_JOBS = AppProtoEnums.APP_OP_RUN_LONG_JOBS;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 122;
+    public static final int _NUM_OP = 123;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1839,6 +1846,13 @@
     public static final String OPSTR_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO =
             "android:receive_explicit_user_interaction_audio";
 
+    /**
+     * App can schedule long running jobs.
+     *
+     * @hide
+     */
+    public static final String OPSTR_RUN_LONG_JOBS = "android:run_long_jobs";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1933,6 +1947,7 @@
             OP_SCHEDULE_EXACT_ALARM,
             OP_MANAGE_MEDIA,
             OP_TURN_SCREEN_ON,
+            OP_RUN_LONG_JOBS,
     };
 
     static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
@@ -2312,7 +2327,9 @@
         new AppOpInfo.Builder(OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
                 OPSTR_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
                 "RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO").setDefaultMode(
-                AppOpsManager.MODE_ALLOWED).build()
+                AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_RUN_LONG_JOBS, OPSTR_RUN_LONG_JOBS, "RUN_LONG_JOBS")
+                .setPermission(Manifest.permission.RUN_LONG_JOBS).build()
     };
 
     /**
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 48638d1..45d4458 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -636,6 +636,7 @@
      * @param broadcastIsInteractive
      * @see #isInteractiveBroadcast()
      */
+    @RequiresPermission(android.Manifest.permission.BROADCAST_OPTION_INTERACTIVE)
     public void setInteractiveBroadcast(boolean broadcastIsInteractive) {
         mIsInteractiveBroadcast = broadcastIsInteractive;
     }
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index f156b30..f674e88 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -56,7 +56,7 @@
      * The package this op applies to
      * @hide
      */
-    private final @NonNull String mPackageName;
+    private final @Nullable String mPackageName;
 
     /**
      * Native code relies on parcel ordering, do not change
@@ -64,7 +64,7 @@
      */
     @TestApi
     public SyncNotedAppOp(int opMode, @IntRange(from = 0L) int opCode,
-            @Nullable String attributionTag, @NonNull String packageName) {
+            @Nullable String attributionTag, @Nullable String packageName) {
         this.mOpCode = opCode;
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mOpCode,
@@ -101,7 +101,7 @@
      * @hide
      */
     public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String attributionTag,
-            @NonNull String packageName) {
+            @Nullable String packageName) {
         this(AppOpsManager.MODE_IGNORED, opCode, attributionTag, packageName);
     }
 
@@ -152,7 +152,7 @@
      * @hide
      */
     @DataClass.Generated.Member
-    public @NonNull String getPackageName() {
+    public @Nullable String getPackageName() {
         return mPackageName;
     }
 
@@ -211,11 +211,12 @@
 
         byte flg = 0;
         if (mAttributionTag != null) flg |= 0x4;
+        if (mPackageName != null) flg |= 0x8;
         dest.writeByte(flg);
         dest.writeInt(mOpMode);
         dest.writeInt(mOpCode);
         if (mAttributionTag != null) dest.writeString(mAttributionTag);
-        dest.writeString(mPackageName);
+        if (mPackageName != null) dest.writeString(mPackageName);
     }
 
     @Override
@@ -233,7 +234,7 @@
         int opMode = in.readInt();
         int opCode = in.readInt();
         String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
-        String packageName = in.readString();
+        String packageName = (flg & 0x8) == 0 ? null : in.readString();
 
         this.mOpMode = opMode;
         this.mOpCode = opCode;
@@ -243,8 +244,6 @@
                 "to", AppOpsManager._NUM_OP - 1);
         this.mAttributionTag = attributionTag;
         this.mPackageName = packageName;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mPackageName);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -264,10 +263,10 @@
     };
 
     @DataClass.Generated(
-            time = 1643320427700L,
+            time = 1667247337573L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java",
-            inputSignatures = "private final  int mOpMode\nprivate final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mPackageName\npublic @android.annotation.NonNull java.lang.String getOp()\npublic  int getOpMode()\nprivate  java.lang.String opCodeToString()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genConstructor=false, genToString=true)")
+            inputSignatures = "private final  int mOpMode\nprivate final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.Nullable java.lang.String mPackageName\npublic @android.annotation.NonNull java.lang.String getOp()\npublic  int getOpMode()\nprivate  java.lang.String opCodeToString()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genConstructor=false, genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 08a6b8c..aaa3d21 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -173,6 +173,7 @@
 import android.os.IUserManager;
 import android.os.IncidentManager;
 import android.os.PerformanceHintManager;
+import android.os.PermissionEnforcer;
 import android.os.PowerManager;
 import android.os.RecoverySystem;
 import android.os.ServiceManager;
@@ -1366,6 +1367,14 @@
                         return new PermissionCheckerManager(ctx.getOuterContext());
                     }});
 
+        registerService(Context.PERMISSION_ENFORCER_SERVICE, PermissionEnforcer.class,
+                new CachedServiceFetcher<PermissionEnforcer>() {
+                    @Override
+                    public PermissionEnforcer createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        return new PermissionEnforcer(ctx.getOuterContext());
+                    }});
+
         registerService(Context.DYNAMIC_SYSTEM_SERVICE, DynamicSystemManager.class,
                 new CachedServiceFetcher<DynamicSystemManager>() {
                     @Override
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 95e9c22..5e15b0a 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -2456,7 +2456,12 @@
 
         public void waitForCompletion() {
             try {
-                mLatch.await(30, TimeUnit.SECONDS);
+                final boolean completed = mLatch.await(30, TimeUnit.SECONDS);
+                if (completed) {
+                    Log.d(TAG, "Wallpaper set completion.");
+                } else {
+                    Log.d(TAG, "Timeout waiting for wallpaper set completion!");
+                }
             } catch (InterruptedException e) {
                 // This might be legit: the crop may take a very long time. Don't sweat
                 // it in that case; we are okay with display lagging behind in order to
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index de19687..f4cee5a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3864,6 +3864,56 @@
     public static final String EXTRA_RESOURCE_IDS =
             "android.app.extra.RESOURCE_IDS";
 
+    /** Allow the user to choose whether to enable MTE on the device. */
+    public static final int MTE_NOT_CONTROLLED_BY_POLICY = 0;
+
+    /**
+     * Require that MTE be enabled on the device, if supported. Can be set by a device owner or a
+     * profile owner of an organization-owned managed profile.
+     */
+    public static final int MTE_ENABLED = 1;
+
+    /** Require that MTE be disabled on the device. Can be set by a device owner. */
+    public static final int MTE_DISABLED = 2;
+
+    /** @hide */
+    @IntDef(
+            prefix = {"MTE_"},
+            value = {MTE_ENABLED, MTE_DISABLED, MTE_NOT_CONTROLLED_BY_POLICY})
+    @Retention(RetentionPolicy.SOURCE)
+    public static @interface MtePolicy {}
+
+    /**
+     * Set MTE policy for device. MTE_ENABLED does not necessarily enable MTE if set on a device
+     * that does not support MTE.
+     *
+     * The default policy is MTE_NOT_CONTROLLED_BY_POLICY.
+     *
+     * Memory Tagging Extension (MTE) is a CPU extension that allows to protect against certain
+     * classes of security problems at a small runtime performance cost overhead.
+     *
+     * @param policy the policy to be set
+     */
+    public void setMtePolicy(@MtePolicy int policy) {
+        // TODO(b/244290023): implement
+        // This is SecurityException to temporarily make ParentProfileTest happy.
+        // This is not used.
+        throw new SecurityException("not implemented");
+    }
+
+    /**
+     * Get currently set MTE policy. This is not necessarily the same as the state of MTE on the
+     * device, as the device might not support MTE.
+     *
+     * @return the currently set policy
+     */
+    public @MtePolicy int getMtePolicy() {
+        // TODO(b/244290023): implement
+        // This is SecurityException to temporarily make ParentProfileTest happy.
+        // This is not used.
+        throw new SecurityException("not implemented");
+    }
+
     /**
      * This object is a single place to tack on invalidation and disable calls.  All
      * binder caches in this class derive from this Config, so all can be invalidated or
diff --git a/core/java/android/app/backup/BackupRestoreEventLogger.java b/core/java/android/app/backup/BackupRestoreEventLogger.java
index b789b38..760c6f0 100644
--- a/core/java/android/app/backup/BackupRestoreEventLogger.java
+++ b/core/java/android/app/backup/BackupRestoreEventLogger.java
@@ -19,10 +19,15 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.util.Slog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -38,6 +43,8 @@
  * @hide
  */
 public class BackupRestoreEventLogger {
+    private static final String TAG = "BackupRestoreEventLogger";
+
     /**
      * Max number of unique data types for which an instance of this logger can store info. Attempts
      * to use more distinct data type values will be rejected.
@@ -72,6 +79,8 @@
     public @interface BackupRestoreError {}
 
     private final int mOperationType;
+    private final Map<String, DataTypeResult> mResults = new HashMap<>();
+    private final MessageDigest mHashDigest;
 
     /**
      * @param operationType type of the operation for which logging will be performed. See
@@ -81,6 +90,14 @@
      */
     public BackupRestoreEventLogger(@OperationType int operationType) {
         mOperationType = operationType;
+
+        MessageDigest hashDigest = null;
+        try {
+            hashDigest = MessageDigest.getInstance("SHA-256");
+        } catch (NoSuchAlgorithmException e) {
+            Slog.w("Couldn't create MessageDigest for hash computation", e);
+        }
+        mHashDigest = hashDigest;
     }
 
     /**
@@ -98,7 +115,7 @@
      * @return boolean, indicating whether the log has been accepted.
      */
     public boolean logItemsBackedUp(@NonNull @BackupRestoreDataType String dataType, int count) {
-        return true;
+        return logSuccess(OperationType.BACKUP, dataType, count);
     }
 
     /**
@@ -118,7 +135,7 @@
      */
     public boolean logItemsBackupFailed(@NonNull @BackupRestoreDataType String dataType, int count,
             @Nullable @BackupRestoreError String error) {
-        return true;
+        return logFailure(OperationType.BACKUP, dataType, count, error);
     }
 
     /**
@@ -139,7 +156,7 @@
      */
     public boolean logBackupMetaData(@NonNull @BackupRestoreDataType String dataType,
             @NonNull String metaData) {
-        return true;
+        return logMetaData(OperationType.BACKUP, dataType, metaData);
     }
 
     /**
@@ -159,7 +176,7 @@
      * @return boolean, indicating whether the log has been accepted.
      */
     public boolean logItemsRestored(@NonNull @BackupRestoreDataType String dataType, int count) {
-        return true;
+        return logSuccess(OperationType.RESTORE, dataType, count);
     }
 
     /**
@@ -181,7 +198,7 @@
      */
     public boolean logItemsRestoreFailed(@NonNull @BackupRestoreDataType String dataType, int count,
             @Nullable @BackupRestoreError String error) {
-        return true;
+        return logFailure(OperationType.RESTORE, dataType, count, error);
     }
 
     /**
@@ -204,7 +221,7 @@
      */
     public boolean logRestoreMetadata(@NonNull @BackupRestoreDataType String dataType,
             @NonNull  String metadata) {
-        return true;
+        return logMetaData(OperationType.RESTORE, dataType, metadata);
     }
 
     /**
@@ -214,7 +231,7 @@
      * @hide
      */
     public List<DataTypeResult> getLoggingResults() {
-        return Collections.emptyList();
+        return new ArrayList<>(mResults.values());
     }
 
     /**
@@ -227,22 +244,97 @@
         return mOperationType;
     }
 
+    private boolean logSuccess(@OperationType int operationType,
+            @BackupRestoreDataType String dataType, int count) {
+        DataTypeResult dataTypeResult = getDataTypeResult(operationType, dataType);
+        if (dataTypeResult == null) {
+            return false;
+        }
+
+        dataTypeResult.mSuccessCount += count;
+        mResults.put(dataType, dataTypeResult);
+
+        return true;
+    }
+
+    private boolean logFailure(@OperationType int operationType,
+            @NonNull @BackupRestoreDataType String dataType, int count,
+            @Nullable @BackupRestoreError String error) {
+        DataTypeResult dataTypeResult = getDataTypeResult(operationType, dataType);
+        if (dataTypeResult == null) {
+            return false;
+        }
+
+        dataTypeResult.mFailCount += count;
+        if (error != null) {
+            dataTypeResult.mErrors.merge(error, count, Integer::sum);
+        }
+
+        return true;
+    }
+
+    private boolean logMetaData(@OperationType int operationType,
+            @NonNull @BackupRestoreDataType String dataType, @NonNull String metaData) {
+        if (mHashDigest == null) {
+            return false;
+        }
+        DataTypeResult dataTypeResult = getDataTypeResult(operationType, dataType);
+        if (dataTypeResult == null) {
+            return false;
+        }
+
+        dataTypeResult.mMetadataHash = getMetaDataHash(metaData);
+
+        return true;
+    }
+
+    /**
+     * Get the result container for the given data type.
+     *
+     * @return {@code DataTypeResult} object corresponding to the given {@code dataType} or
+     *         {@code null} if the logger can't accept logs for the given data type.
+     */
+    @Nullable
+    private DataTypeResult getDataTypeResult(@OperationType int operationType,
+            @BackupRestoreDataType String dataType) {
+        if (operationType != mOperationType) {
+            // Operation type for which we're trying to record logs doesn't match the operation
+            // type for which this logger instance was created.
+            Slog.d(TAG, "Operation type mismatch: logger created for " + mOperationType
+                    + ", trying to log for " + operationType);
+            return null;
+        }
+
+        if (!mResults.containsKey(dataType)) {
+            if (mResults.keySet().size() == DATA_TYPES_ALLOWED) {
+                // This is a new data type and we're already at capacity.
+                Slog.d(TAG, "Logger is full, ignoring new data type");
+                return null;
+            }
+
+            mResults.put(dataType,  new DataTypeResult(dataType));
+        }
+
+        return mResults.get(dataType);
+    }
+
+    private byte[] getMetaDataHash(String metaData) {
+        return mHashDigest.digest(metaData.getBytes(StandardCharsets.UTF_8));
+    }
+
     /**
      * Encapsulate logging results for a single data type.
      */
     public static class DataTypeResult {
         @BackupRestoreDataType
         private final String mDataType;
-        private final int mSuccessCount;
-        private final Map<String, Integer> mErrors;
-        private final byte[] mMetadataHash;
+        private int mSuccessCount;
+        private int mFailCount;
+        private final Map<String, Integer> mErrors = new HashMap<>();
+        private byte[] mMetadataHash;
 
-        public DataTypeResult(String dataType, int successCount,
-                Map<String, Integer> errors, byte[] metadataHash) {
+        public DataTypeResult(String dataType) {
             mDataType = dataType;
-            mSuccessCount = successCount;
-            mErrors = errors;
-            mMetadataHash = metadataHash;
         }
 
         @NonNull
@@ -260,6 +352,13 @@
         }
 
         /**
+         * @return number of items of the given data type that have failed to back up or restore.
+         */
+        public int getFailCount() {
+            return mFailCount;
+        }
+
+        /**
          * @return mapping of {@link BackupRestoreError} to the count of items that are affected by
          *         the error.
          */
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d65210b..1df0fa8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5142,6 +5142,14 @@
     public static final String PERMISSION_CHECKER_SERVICE = "permission_checker";
 
     /**
+     * Official published name of the (internal) permission enforcer service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String PERMISSION_ENFORCER_SERVICE = "permission_enforcer";
+
+    /**
      * Use with {@link #getSystemService(String) to retrieve an
      * {@link android.apphibernation.AppHibernationManager}} for
      * communicating with the hibernation service.
@@ -5194,6 +5202,15 @@
     public static final String DROPBOX_SERVICE = "dropbox";
 
     /**
+     * System service name for BackgroundInstallControlService. This service supervises the MBAs
+     * on device and provides the related metadata of the MBAs.
+     *
+     * @hide
+     */
+    @SuppressLint("ServiceName")
+    public static final String BACKGROUND_INSTALL_CONTROL_SERVICE = "background_install_control";
+
+    /**
      * System service name for BinaryTransparencyService. This is used to retrieve measurements
      * pertaining to various pre-installed and system binaries on device for the purposes of
      * providing transparency to the user.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt b/core/java/android/content/pm/IBackgroundInstallControlService.aidl
similarity index 72%
copy from packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
copy to core/java/android/content/pm/IBackgroundInstallControlService.aidl
index 581dafa3..c8e7cae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
+++ b/core/java/android/content/pm/IBackgroundInstallControlService.aidl
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.model
+package android.content.pm;
 
-/** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
-enum class KeyguardQuickAffordancePosition {
-    BOTTOM_START,
-    BOTTOM_END,
+import android.content.pm.ParceledListSlice;
+
+/**
+ * {@hide}
+ */
+interface IBackgroundInstallControlService {
+    ParceledListSlice getBackgroundInstalledPackages(long flags, int userId);
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d2fb1fb..d7686e2 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -3346,7 +3346,7 @@
         /**
          * The label representing the app to be installed.
          */
-        private final @NonNull String mLabel;
+        private final @NonNull CharSequence mLabel;
         /**
          * The locale of the app label being used.
          */
@@ -3388,7 +3388,7 @@
         @DataClass.Generated.Member
         public PreapprovalDetails(
                 @Nullable Bitmap icon,
-                @NonNull String label,
+                @NonNull CharSequence label,
                 @NonNull ULocale locale,
                 @NonNull String packageName) {
             this.mIcon = icon;
@@ -3417,7 +3417,7 @@
          * The label representing the app to be installed.
          */
         @DataClass.Generated.Member
-        public @NonNull String getLabel() {
+        public @NonNull CharSequence getLabel() {
             return mLabel;
         }
 
@@ -3461,7 +3461,7 @@
             if (mIcon != null) flg |= 0x1;
             dest.writeByte(flg);
             if (mIcon != null) mIcon.writeToParcel(dest, flags);
-            dest.writeString8(mLabel);
+            dest.writeCharSequence(mLabel);
             dest.writeString8(mLocale.toString());
             dest.writeString8(mPackageName);
         }
@@ -3479,7 +3479,7 @@
 
             byte flg = in.readByte();
             Bitmap icon = (flg & 0x1) == 0 ? null : Bitmap.CREATOR.createFromParcel(in);
-            String label = in.readString8();
+            CharSequence label = (CharSequence) in.readCharSequence();
             ULocale locale = new ULocale(in.readString8());
             String packageName = in.readString8();
 
@@ -3519,7 +3519,7 @@
         public static final class Builder {
 
             private @Nullable Bitmap mIcon;
-            private @NonNull String mLabel;
+            private @NonNull CharSequence mLabel;
             private @NonNull ULocale mLocale;
             private @NonNull String mPackageName;
 
@@ -3545,7 +3545,7 @@
              * The label representing the app to be installed.
              */
             @DataClass.Generated.Member
-            public @NonNull Builder setLabel(@NonNull String value) {
+            public @NonNull Builder setLabel(@NonNull CharSequence value) {
                 checkNotUsed();
                 mBuilderFieldsSet |= 0x2;
                 mLabel = value;
@@ -3596,10 +3596,10 @@
         }
 
         @DataClass.Generated(
-                time = 1664257135109L,
+                time = 1666748098353L,
                 codegenVersion = "1.0.23",
                 sourceFile = "frameworks/base/core/java/android/content/pm/PackageInstaller.java",
-                inputSignatures = "private final @android.annotation.Nullable android.graphics.Bitmap mIcon\nprivate final @android.annotation.NonNull java.lang.String mLabel\nprivate final @android.annotation.NonNull android.icu.util.ULocale mLocale\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nclass PreapprovalDetails extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genHiddenConstructor=true, genBuilder=true, genToString=true)")
+                inputSignatures = "private final @android.annotation.Nullable android.graphics.Bitmap mIcon\nprivate final @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate final @android.annotation.NonNull android.icu.util.ULocale mLocale\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nclass PreapprovalDetails extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genHiddenConstructor=true, genBuilder=true, genToString=true)")
         @Deprecated
         private void __metadata() {}
 
diff --git a/core/java/android/credentials/Credential.java b/core/java/android/credentials/Credential.java
index fed2592..db89170 100644
--- a/core/java/android/credentials/Credential.java
+++ b/core/java/android/credentials/Credential.java
@@ -36,7 +36,8 @@
      *
      * @hide
      */
-    @NonNull public static final String TYPE_PASSWORD = "android.credentials.TYPE_PASSWORD";
+    @NonNull public static final String TYPE_PASSWORD_CREDENTIAL =
+            "android.credentials.TYPE_PASSWORD_CREDENTIAL";
 
     /**
      * The credential type.
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index b9cef0f..30ee118 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -85,7 +85,7 @@
         ICancellationSignal cancelRemote = null;
         try {
             cancelRemote = mService.executeGetCredential(request,
-                    new GetCredentialTransport(executor, callback));
+                    new GetCredentialTransport(executor, callback), mContext.getOpPackageName());
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
@@ -124,7 +124,8 @@
         ICancellationSignal cancelRemote = null;
         try {
             cancelRemote = mService.executeCreateCredential(request,
-                    new CreateCredentialTransport(executor, callback));
+                    new CreateCredentialTransport(executor, callback),
+                    mContext.getOpPackageName());
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index dcf7106..b0f27f9 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -29,7 +29,7 @@
  */
 interface ICredentialManager {
 
-    @nullable ICancellationSignal executeGetCredential(in GetCredentialRequest request, in IGetCredentialCallback callback);
+    @nullable ICancellationSignal executeGetCredential(in GetCredentialRequest request, in IGetCredentialCallback callback, String callingPackage);
 
-    @nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback);
+    @nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage);
 }
diff --git a/core/java/android/credentials/ui/ProviderData.java b/core/java/android/credentials/ui/ProviderData.java
index 35e12fa..3728469 100644
--- a/core/java/android/credentials/ui/ProviderData.java
+++ b/core/java/android/credentials/ui/ProviderData.java
@@ -43,10 +43,10 @@
             "android.credentials.ui.extra.PROVIDER_DATA_LIST";
 
     @NonNull
-    private final String mProviderId;
+    private final String mProviderFlattenedComponentName;
     @NonNull
     private final String mProviderDisplayName;
-    @NonNull
+    @Nullable
     private final Icon mIcon;
     @NonNull
     private final List<Entry> mCredentialEntries;
@@ -58,11 +58,11 @@
     private final @CurrentTimeMillisLong long mLastUsedTimeMillis;
 
     public ProviderData(
-            @NonNull String providerId, @NonNull String providerDisplayName,
-            @NonNull Icon icon, @NonNull List<Entry> credentialEntries,
+            @NonNull String providerFlattenedComponentName, @NonNull String providerDisplayName,
+            @Nullable Icon icon, @NonNull List<Entry> credentialEntries,
             @NonNull List<Entry> actionChips, @Nullable Entry authenticationEntry,
             @CurrentTimeMillisLong long lastUsedTimeMillis) {
-        mProviderId = providerId;
+        mProviderFlattenedComponentName = providerFlattenedComponentName;
         mProviderDisplayName = providerDisplayName;
         mIcon = icon;
         mCredentialEntries = credentialEntries;
@@ -73,8 +73,8 @@
 
     /** Returns the unique provider id. */
     @NonNull
-    public String getProviderId() {
-        return mProviderId;
+    public String getProviderFlattenedComponentName() {
+        return mProviderFlattenedComponentName;
     }
 
     @NonNull
@@ -82,7 +82,7 @@
         return mProviderDisplayName;
     }
 
-    @NonNull
+    @Nullable
     public Icon getIcon() {
         return mIcon;
     }
@@ -108,9 +108,9 @@
     }
 
     protected ProviderData(@NonNull Parcel in) {
-        String providerId = in.readString8();
-        mProviderId = providerId;
-        AnnotationValidations.validate(NonNull.class, null, mProviderId);
+        String providerFlattenedComponentName = in.readString8();
+        mProviderFlattenedComponentName = providerFlattenedComponentName;
+        AnnotationValidations.validate(NonNull.class, null, mProviderFlattenedComponentName);
 
         String providerDisplayName = in.readString8();
         mProviderDisplayName = providerDisplayName;
@@ -118,7 +118,6 @@
 
         Icon icon = in.readTypedObject(Icon.CREATOR);
         mIcon = icon;
-        AnnotationValidations.validate(NonNull.class, null, mIcon);
 
         List<Entry> credentialEntries = new ArrayList<>();
         in.readTypedList(credentialEntries, Entry.CREATOR);
@@ -139,7 +138,7 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeString8(mProviderId);
+        dest.writeString8(mProviderFlattenedComponentName);
         dest.writeString8(mProviderDisplayName);
         dest.writeTypedObject(mIcon, flags);
         dest.writeTypedList(mCredentialEntries);
@@ -171,26 +170,27 @@
      * @hide
      */
     public static class Builder {
-        private @NonNull String mProviderId;
+        private @NonNull String mProviderFlattenedComponentName;
         private @NonNull String mProviderDisplayName;
-        private @NonNull Icon mIcon;
+        private @Nullable Icon mIcon;
         private @NonNull List<Entry> mCredentialEntries = new ArrayList<>();
         private @NonNull List<Entry> mActionChips = new ArrayList<>();
         private @Nullable Entry mAuthenticationEntry = null;
         private @CurrentTimeMillisLong long mLastUsedTimeMillis = 0L;
 
         /** Constructor with required properties. */
-        public Builder(@NonNull String providerId, @NonNull String providerDisplayName,
-                @NonNull Icon icon) {
-            mProviderId = providerId;
+        public Builder(@NonNull String providerFlattenedComponentName,
+                @NonNull String providerDisplayName,
+                @Nullable Icon icon) {
+            mProviderFlattenedComponentName = providerFlattenedComponentName;
             mProviderDisplayName = providerDisplayName;
             mIcon = icon;
         }
 
         /** Sets the unique provider id. */
         @NonNull
-        public Builder setProviderId(@NonNull String providerId) {
-            mProviderId = providerId;
+        public Builder setProviderFlattenedComponentName(@NonNull String providerFlattenedComponentName) {
+            mProviderFlattenedComponentName = providerFlattenedComponentName;
             return this;
         }
 
@@ -239,7 +239,8 @@
         /** Builds a {@link ProviderData}. */
         @NonNull
         public ProviderData build() {
-            return new ProviderData(mProviderId, mProviderDisplayName, mIcon, mCredentialEntries,
+            return new ProviderData(mProviderFlattenedComponentName, mProviderDisplayName,
+                    mIcon, mCredentialEntries,
                 mActionChips, mAuthenticationEntry, mLastUsedTimeMillis);
         }
     }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 5403f08..3c73eb6 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -918,6 +918,22 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+    public void setUdfpsOverlay(@NonNull IUdfpsOverlay controller) {
+        if (mService == null) {
+            Slog.w(TAG, "setUdfpsOverlay: no fingerprint service");
+            return;
+        }
+
+        try {
+            mService.setUdfpsOverlay(controller);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     /**
      * Forwards BiometricStateListener to FingerprintService
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 051e3a4..365a6b3 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -26,6 +26,7 @@
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import java.util.List;
@@ -201,6 +202,10 @@
     @EnforcePermission("USE_BIOMETRIC_INTERNAL")
     void setSidefpsController(in ISidefpsController controller);
 
+    // Sets the controller for managing the UDFPS overlay.
+    @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+    void setUdfpsOverlay(in IUdfpsOverlay controller);
+
     // Registers BiometricStateListener.
     @EnforcePermission("USE_BIOMETRIC_INTERNAL")
     void registerBiometricStateListener(IBiometricStateListener listener);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt b/core/java/android/hardware/fingerprint/IUdfpsOverlay.aidl
similarity index 65%
copy from packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
copy to core/java/android/hardware/fingerprint/IUdfpsOverlay.aidl
index 581dafa3..c99fccc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlay.aidl
@@ -14,10 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.model
+package android.hardware.fingerprint;
 
-/** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
-enum class KeyguardQuickAffordancePosition {
-    BOTTOM_START,
-    BOTTOM_END,
+/**
+ * Interface for interacting with the under-display fingerprint sensor (UDFPS) overlay.
+ * @hide
+ */
+oneway interface IUdfpsOverlay {
+    // Shows the overlay.
+    void show(long requestId, int sensorId, int reason);
+
+    // Hides the overlay.
+    void hide(int sensorId);
 }
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index f213224b..49c0f92 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -161,4 +161,11 @@
     void registerBatteryListener(int deviceId, IInputDeviceBatteryListener listener);
 
     void unregisterBatteryListener(int deviceId, IInputDeviceBatteryListener listener);
+
+    // Get the bluetooth address of an input device if known, returning null if it either is not
+    // connected via bluetooth or if the address cannot be determined.
+    @EnforcePermission("BLUETOOTH")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.BLUETOOTH)")
+    String getInputDeviceBluetoothAddress(int deviceId);
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 8d4aac4..0cf15f7 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1481,6 +1481,24 @@
     }
 
     /**
+     * Returns the Bluetooth address of this input device, if known.
+     *
+     * The returned string is always null if this input device is not connected
+     * via Bluetooth, or if the Bluetooth address of the device cannot be
+     * determined. The returned address will look like: "11:22:33:44:55:66".
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @Nullable
+    public String getInputDeviceBluetoothAddress(int deviceId) {
+        try {
+            return mIm.getInputDeviceBluetoothAddress(deviceId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets a vibrator service associated with an input device, always creates a new instance.
      * @return The vibrator, never null.
      * @hide
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 60d8cac..7c2e518 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -108,6 +108,34 @@
     }
 
     /**
+     * This is meant to be called by UsbRequest's queue() in order to synchronize on
+     * UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
+     */
+    /* package */ boolean queueRequest(UsbRequest request, ByteBuffer buffer, int length) {
+        synchronized (mLock) {
+            if (!isOpen()) {
+                return false;
+            }
+
+            return request.queueIfConnectionOpen(buffer, length);
+        }
+    }
+
+    /**
+     * This is meant to be called by UsbRequest's queue() in order to synchronize on
+     * UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
+     */
+    /* package */ boolean queueRequest(UsbRequest request, @Nullable ByteBuffer buffer) {
+        synchronized (mLock) {
+            if (!isOpen()) {
+                return false;
+            }
+
+            return request.queueIfConnectionOpen(buffer);
+        }
+    }
+
+    /**
      * Releases all system resources related to the device.
      * Once the object is closed it cannot be used again.
      * The client must call {@link UsbManager#openDevice} again
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index 6ac5e8d..beb0f8d 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -113,11 +113,13 @@
      * Releases all resources related to this request.
      */
     public void close() {
-        if (mNativeContext != 0) {
-            mEndpoint = null;
-            mConnection = null;
-            native_close();
-            mCloseGuard.close();
+        synchronized (mLock) {
+            if (mNativeContext != 0) {
+                mEndpoint = null;
+                mConnection = null;
+                native_close();
+                mCloseGuard.close();
+            }
         }
     }
 
@@ -191,10 +193,32 @@
      */
     @Deprecated
     public boolean queue(ByteBuffer buffer, int length) {
+        UsbDeviceConnection connection = mConnection;
+        if (connection == null) {
+            // The expected exception by CTS Verifier - USB Device test
+            throw new NullPointerException("invalid connection");
+        }
+
+        // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
+        // the connection being closed while queueing.
+        return connection.queueRequest(this, buffer, length);
+    }
+
+    /**
+     * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
+     * there, to prevent the connection being closed while queueing.
+     */
+    /* package */ boolean queueIfConnectionOpen(ByteBuffer buffer, int length) {
+        UsbDeviceConnection connection = mConnection;
+        if (connection == null || !connection.isOpen()) {
+            // The expected exception by CTS Verifier - USB Device test
+            throw new NullPointerException("invalid connection");
+        }
+
         boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
         boolean result;
 
-        if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+        if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
                 && length > MAX_USBFS_BUFFER_SIZE) {
             length = MAX_USBFS_BUFFER_SIZE;
         }
@@ -243,6 +267,28 @@
      * @return true if the queueing operation succeeded
      */
     public boolean queue(@Nullable ByteBuffer buffer) {
+        UsbDeviceConnection connection = mConnection;
+        if (connection == null) {
+            // The expected exception by CTS Verifier - USB Device test
+            throw new IllegalStateException("invalid connection");
+        }
+
+        // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
+        // the connection being closed while queueing.
+        return connection.queueRequest(this, buffer);
+    }
+
+    /**
+     * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
+     * there, to prevent the connection being closed while queueing.
+     */
+    /* package */ boolean queueIfConnectionOpen(@Nullable ByteBuffer buffer) {
+        UsbDeviceConnection connection = mConnection;
+        if (connection == null || !connection.isOpen()) {
+            // The expected exception by CTS Verifier - USB Device test
+            throw new IllegalStateException("invalid connection");
+        }
+
         // Request need to be initialized
         Preconditions.checkState(mNativeContext != 0, "request is not initialized");
 
@@ -260,7 +306,7 @@
                 mIsUsingNewQueue = true;
                 wasQueued = native_queue(null, 0, 0);
             } else {
-                if (mConnection.getContext().getApplicationInfo().targetSdkVersion
+                if (connection.getContext().getApplicationInfo().targetSdkVersion
                         < Build.VERSION_CODES.P) {
                     // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
                     Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
@@ -363,11 +409,12 @@
      * @return true if cancelling succeeded
      */
     public boolean cancel() {
-        if (mConnection == null) {
+        UsbDeviceConnection connection = mConnection;
+        if (connection == null) {
             return false;
         }
 
-        return mConnection.cancelRequest(this);
+        return connection.cancelRequest(this);
     }
 
     /**
@@ -382,7 +429,8 @@
      * @return true if cancelling succeeded.
      */
     /* package */ boolean cancelIfOpen() {
-        if (mNativeContext == 0 || (mConnection != null && !mConnection.isOpen())) {
+        UsbDeviceConnection connection = mConnection;
+        if (mNativeContext == 0 || (connection != null && !connection.isOpen())) {
             Log.w(TAG,
                     "Detected attempt to cancel a request on a connection which isn't open");
             return false;
diff --git a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
index 4f09bee..95aecfe 100644
--- a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
+++ b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
@@ -16,30 +16,29 @@
 
 package android.inputmethodservice;
 
+import static android.view.inputmethod.TextBoundsInfoResult.CODE_CANCELLED;
+
 import android.annotation.AnyThread;
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.RectF;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.view.KeyEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
-import android.view.inputmethod.DeleteGesture;
-import android.view.inputmethod.DeleteRangeGesture;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.HandwritingGesture;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputContentInfo;
-import android.view.inputmethod.InsertGesture;
-import android.view.inputmethod.JoinOrSplitGesture;
-import android.view.inputmethod.RemoveSpaceGesture;
-import android.view.inputmethod.SelectGesture;
-import android.view.inputmethod.SelectRangeGesture;
+import android.view.inputmethod.ParcelableHandwritingGesture;
 import android.view.inputmethod.SurroundingText;
 import android.view.inputmethod.TextAttribute;
+import android.view.inputmethod.TextBoundsInfo;
+import android.view.inputmethod.TextBoundsInfoResult;
 
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.inputmethod.IRemoteInputConnection;
@@ -47,6 +46,7 @@
 
 import java.util.Objects;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 
 /**
@@ -98,6 +98,44 @@
     };
 
     /**
+     * Subclass of {@link ResultReceiver} used by
+     * {@link #requestTextBoundsInfo(RectF, Executor, Consumer)} for providing
+     * callback.
+     */
+    private static final class TextBoundsInfoResultReceiver extends ResultReceiver {
+        @Nullable
+        private Consumer<TextBoundsInfoResult> mConsumer;
+        @Nullable
+        private Executor mExecutor;
+
+        TextBoundsInfoResultReceiver(@NonNull Executor executor,
+                @NonNull Consumer<TextBoundsInfoResult> consumer) {
+            super(null);
+            mExecutor = executor;
+            mConsumer = consumer;
+        }
+
+        @Override
+        protected void onReceiveResult(@TextBoundsInfoResult.ResultCode int resultCode,
+                @Nullable Bundle resultData) {
+            synchronized (this) {
+                if (mExecutor != null && mConsumer != null) {
+                    final TextBoundsInfoResult textBoundsInfoResult = new TextBoundsInfoResult(
+                            resultCode, TextBoundsInfo.createFromBundle(resultData));
+                    mExecutor.execute(() -> mConsumer.accept(textBoundsInfoResult));
+                    // provide callback only once.
+                    clear();
+                }
+            }
+        }
+
+        private void clear() {
+            mExecutor = null;
+            mConsumer = null;
+        }
+    }
+
+    /**
      * Creates a new instance of {@link IRemoteInputConnectionInvoker} for the given
      * {@link IRemoteInputConnection}.
      *
@@ -637,50 +675,19 @@
     }
 
     /**
-     * Invokes one of {@link IRemoteInputConnection#performHandwritingSelectGesture},
-     * {@link IRemoteInputConnection#performHandwritingSelectRangeGesture},
-     * {@link IRemoteInputConnection#performHandwritingDeleteGesture},
-     * {@link IRemoteInputConnection#performHandwritingDeleteRangeGesture},
-     * {@link IRemoteInputConnection#performHandwritingInsertGesture},
-     * {@link IRemoteInputConnection#performHandwritingRemoveSpaceGesture},
-     * {@link IRemoteInputConnection#performHandwritingJoinOrSplitGesture}.
+     * Invokes {@link IRemoteInputConnection#performHandwritingGesture(
+     * InputConnectionCommandHeader, ParcelableHandwritingGesture, ResultReceiver)}.
      */
     @AnyThread
-    public void performHandwritingGesture(
-            @NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
-            @Nullable IntConsumer consumer) {
-
+    public void performHandwritingGesture(@NonNull ParcelableHandwritingGesture gesture,
+            @Nullable @CallbackExecutor Executor executor, @Nullable IntConsumer consumer) {
         ResultReceiver resultReceiver = null;
         if (consumer != null) {
             Objects.requireNonNull(executor);
             resultReceiver = new IntResultReceiver(executor, consumer);
         }
         try {
-            if (gesture instanceof SelectGesture) {
-                mConnection.performHandwritingSelectGesture(
-                        createHeader(), (SelectGesture) gesture, resultReceiver);
-            } else if (gesture instanceof SelectRangeGesture) {
-                mConnection.performHandwritingSelectRangeGesture(
-                        createHeader(), (SelectRangeGesture) gesture, resultReceiver);
-            } else if (gesture instanceof InsertGesture) {
-                mConnection.performHandwritingInsertGesture(
-                        createHeader(), (InsertGesture) gesture, resultReceiver);
-            } else if (gesture instanceof DeleteGesture) {
-                mConnection.performHandwritingDeleteGesture(
-                        createHeader(), (DeleteGesture) gesture, resultReceiver);
-            } else if (gesture instanceof DeleteRangeGesture) {
-                mConnection.performHandwritingDeleteRangeGesture(
-                        createHeader(), (DeleteRangeGesture) gesture, resultReceiver);
-            } else if (gesture instanceof RemoveSpaceGesture) {
-                mConnection.performHandwritingRemoveSpaceGesture(
-                        createHeader(), (RemoveSpaceGesture) gesture, resultReceiver);
-            } else if (gesture instanceof JoinOrSplitGesture) {
-                mConnection.performHandwritingJoinOrSplitGesture(
-                        createHeader(), (JoinOrSplitGesture) gesture, resultReceiver);
-            } else if (consumer != null && executor != null) {
-                executor.execute(()
-                        -> consumer.accept(InputConnection.HANDWRITING_GESTURE_RESULT_UNSUPPORTED));
-            }
+            mConnection.performHandwritingGesture(createHeader(), gesture, resultReceiver);
         } catch (RemoteException e) {
             if (consumer != null && executor != null) {
                 executor.execute(() -> consumer.accept(
@@ -736,6 +743,28 @@
     }
 
     /**
+     * Invokes {@link IRemoteInputConnection#requestTextBoundsInfo(InputConnectionCommandHeader,
+     * RectF, ResultReceiver)}
+     * @param rectF {@code rectF} parameter to be passed.
+     * @param executor {@code Executor} parameter to be passed.
+     * @param consumer {@code Consumer} parameter to be passed.
+     */
+    @AnyThread
+    public void requestTextBoundsInfo(
+            @NonNull RectF rectF, @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<TextBoundsInfoResult> consumer) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(consumer);
+
+        final ResultReceiver resultReceiver = new TextBoundsInfoResultReceiver(executor, consumer);
+        try {
+            mConnection.requestTextBoundsInfo(createHeader(), rectF, resultReceiver);
+        } catch (RemoteException e) {
+            executor.execute(() -> consumer.accept(new TextBoundsInfoResult(CODE_CANCELLED)));
+        }
+    }
+
+    /**
      * Invokes {@link IRemoteInputConnection#commitContent(InputConnectionCommandHeader,
      * InputContentInfo, int, Bundle, AndroidFuture)}.
      *
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 09e86c4..976e71f 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -21,6 +21,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.RectF;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
@@ -32,8 +33,10 @@
 import android.view.inputmethod.HandwritingGesture;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.ParcelableHandwritingGesture;
 import android.view.inputmethod.SurroundingText;
 import android.view.inputmethod.TextAttribute;
+import android.view.inputmethod.TextBoundsInfoResult;
 
 import com.android.internal.inputmethod.CancellationGroup;
 import com.android.internal.inputmethod.CompletableFutureUtil;
@@ -44,6 +47,7 @@
 import java.lang.ref.WeakReference;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 
 /**
@@ -418,7 +422,8 @@
     public void performHandwritingGesture(
             @NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
             @Nullable IntConsumer consumer) {
-        mInvoker.performHandwritingGesture(gesture, executor, consumer);
+        mInvoker.performHandwritingGesture(ParcelableHandwritingGesture.of(gesture), executor,
+                consumer);
     }
 
     @AnyThread
@@ -460,6 +465,13 @@
     }
 
     @AnyThread
+    public void requestTextBoundsInfo(
+            @NonNull RectF rectF, @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<TextBoundsInfoResult> consumer) {
+        mInvoker.requestTextBoundsInfo(rectF, executor, consumer);
+    }
+
+    @AnyThread
     public Handler getHandler() {
         // Nothing should happen when called from input method.
         return null;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index d3a6323..3c4abab 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -562,7 +562,7 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
     public final native void markVintfStability();
 
     /**
@@ -1219,25 +1219,40 @@
     @UnsupportedAppUsage
     private boolean execTransact(int code, long dataObj, long replyObj,
             int flags) {
+
+        Parcel data = Parcel.obtain(dataObj);
+        Parcel reply = Parcel.obtain(replyObj);
+
         // At that point, the parcel request headers haven't been parsed so we do not know what
         // {@link WorkSource} the caller has set. Use calling UID as the default.
-        final int callingUid = Binder.getCallingUid();
-        final long origWorkSource = ThreadLocalWorkSource.setUid(callingUid);
+        //
+        // TODO: this is wrong - we should attribute along the entire call route
+        // also this attribution logic should move to native code - it only works
+        // for Java now
+        //
+        // This attribution support is not generic and therefore not support in RPC mode
+        final int callingUid = data.isForRpc() ? -1 : Binder.getCallingUid();
+        final long origWorkSource = callingUid == -1
+                ? -1 : ThreadLocalWorkSource.setUid(callingUid);
+
         try {
-            return execTransactInternal(code, dataObj, replyObj, flags, callingUid);
+            return execTransactInternal(code, data, reply, flags, callingUid);
         } finally {
-            ThreadLocalWorkSource.restore(origWorkSource);
+            reply.recycle();
+            data.recycle();
+
+            if (callingUid != -1) {
+                ThreadLocalWorkSource.restore(origWorkSource);
+            }
         }
     }
 
-    private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags,
+    private boolean execTransactInternal(int code, Parcel data, Parcel reply, int flags,
             int callingUid) {
         // Make sure the observer won't change while processing a transaction.
         final BinderInternal.Observer observer = sObserver;
         final CallSession callSession =
                 observer != null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null;
-        Parcel data = Parcel.obtain(dataObj);
-        Parcel reply = Parcel.obtain(replyObj);
         // Theoretically, we should call transact, which will call onTransact,
         // but all that does is rewind it, and we just got these from an IPC,
         // so we'll just call it directly.
@@ -1268,8 +1283,10 @@
 
         final boolean tracingEnabled = tagEnabled && transactionTraceName != null;
         try {
+            // TODO - this logic should not be in Java - it should be in native
+            // code in libbinder so that it works for all binder users.
             final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
-            if (heavyHitterWatcher != null) {
+            if (heavyHitterWatcher != null && callingUid != -1) {
                 // Notify the heavy hitter watcher, if it's enabled.
                 heavyHitterWatcher.onTransaction(callingUid, getClass(), code);
             }
@@ -1277,7 +1294,10 @@
                 Trace.traceBegin(Trace.TRACE_TAG_AIDL, transactionTraceName);
             }
 
-            if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0) {
+            // TODO - this logic should not be in Java - it should be in native
+            // code in libbinder so that it works for all binder users. Further,
+            // this should not re-use flags.
+            if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0 && callingUid != -1) {
                 AppOpsManager.startNotedAppOpsCollection(callingUid);
                 try {
                     res = onTransact(code, data, reply, flags);
@@ -1320,8 +1340,6 @@
             }
 
             checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
-            reply.recycle();
-            data.recycle();
         }
 
         // Just in case -- we are done with the IPC, so there should be no more strict
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index d451765..1673ade 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -367,6 +367,8 @@
     @FastNative
     private static native void nativeMarkForBinder(long nativePtr, IBinder binder);
     @CriticalNative
+    private static native boolean nativeIsForRpc(long nativePtr);
+    @CriticalNative
     private static native int nativeDataSize(long nativePtr);
     @CriticalNative
     private static native int nativeDataAvail(long nativePtr);
@@ -559,9 +561,11 @@
      */
     public final void recycle() {
         if (mRecycled) {
-            Log.w(TAG, "Recycle called on unowned Parcel. (recycle twice?) Here: "
+            Log.wtf(TAG, "Recycle called on unowned Parcel. (recycle twice?) Here: "
                     + Log.getStackTraceString(new Throwable())
                     + " Original recycle call (if DEBUG_RECYCLE): ", mStack);
+
+            return;
         }
         mRecycled = true;
 
@@ -644,6 +648,15 @@
         nativeMarkForBinder(mNativePtr, binder);
     }
 
+    /**
+     * Whether this Parcel is written for an RPC transaction.
+     *
+     * @hide
+     */
+    public final boolean isForRpc() {
+        return nativeIsForRpc(mNativePtr);
+    }
+
     /** @hide */
     @ParcelFlags
     @TestApi
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 8a80457..a2b0486 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -188,7 +188,7 @@
      * @return true if this parcelable is stable.
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
     default @Stability int getStability() {
         return PARCELABLE_STABILITY_LOCAL;
     }
diff --git a/core/java/android/os/PermissionEnforcer.java b/core/java/android/os/PermissionEnforcer.java
new file mode 100644
index 0000000..221e89a
--- /dev/null
+++ b/core/java/android/os/PermissionEnforcer.java
@@ -0,0 +1,101 @@
+/*
+ * 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 android.os;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.content.PermissionChecker;
+import android.permission.PermissionCheckerManager;
+
+/**
+ * PermissionEnforcer check permissions for AIDL-generated services which use
+ * the @EnforcePermission annotation.
+ *
+ * <p>AIDL services may be annotated with @EnforcePermission which will trigger
+ * the generation of permission check code. This generated code relies on
+ * PermissionEnforcer to validate the permissions. The methods available are
+ * purposely similar to the AIDL annotation syntax.
+ *
+ * @see android.permission.PermissionManager
+ *
+ * @hide
+ */
+@SystemService(Context.PERMISSION_ENFORCER_SERVICE)
+public class PermissionEnforcer {
+
+    private final Context mContext;
+
+    /** Protected constructor. Allows subclasses to instantiate an object
+     *  without using a Context.
+     */
+    protected PermissionEnforcer() {
+        mContext = null;
+    }
+
+    /** Constructor, prefer using the fromContext static method when possible */
+    public PermissionEnforcer(@NonNull Context context) {
+        mContext = context;
+    }
+
+    @PermissionCheckerManager.PermissionResult
+    protected int checkPermission(@NonNull String permission, @NonNull AttributionSource source) {
+        return PermissionChecker.checkPermissionForDataDelivery(
+            mContext, permission, PermissionChecker.PID_UNKNOWN, source, "" /* message */);
+    }
+
+    public void enforcePermission(@NonNull String permission, @NonNull
+            AttributionSource source) throws SecurityException {
+        int result = checkPermission(permission, source);
+        if (result != PermissionCheckerManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Access denied, requires: " + permission);
+        }
+    }
+
+    public void enforcePermissionAllOf(@NonNull String[] permissions,
+            @NonNull AttributionSource source) throws SecurityException {
+        for (String permission : permissions) {
+            int result = checkPermission(permission, source);
+            if (result != PermissionCheckerManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Access denied, requires: allOf={"
+                        + String.join(", ", permissions) + "}");
+            }
+        }
+    }
+
+    public void enforcePermissionAnyOf(@NonNull String[] permissions,
+            @NonNull AttributionSource source) throws SecurityException {
+        for (String permission : permissions) {
+            int result = checkPermission(permission, source);
+            if (result == PermissionCheckerManager.PERMISSION_GRANTED) {
+                return;
+            }
+        }
+        throw new SecurityException("Access denied, requires: anyOf={"
+                + String.join(", ", permissions) + "}");
+    }
+
+    /**
+     * Returns a new PermissionEnforcer based on a Context.
+     *
+     * @hide
+     */
+    public static PermissionEnforcer fromContext(@NonNull Context context) {
+        return context.getSystemService(PermissionEnforcer.class);
+    }
+}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 51dc643..f1879dd 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1488,6 +1488,22 @@
     public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
 
     /**
+     * Specifies if a user is not allowed to use 2g networks.
+     *
+     * <p>This restriction can only be set by a device owner or a profile owner of an
+     * organization-owned managed profile on the parent profile.
+     * In all cases, the setting applies globally on the device and will prevent the device from
+     * scanning for or connecting to 2g networks, except in the case of an emergency.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CELLULAR_2G = "no_cellular_2g";
+
+    /**
      * List of key values that can be passed into the various user restriction related methods
      * in {@link UserManager} & {@link DevicePolicyManager}.
      * Note: This is slightly different from the real set of user restrictions listed in {@link
@@ -1568,6 +1584,7 @@
             DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI,
             DISALLOW_WIFI_DIRECT,
             DISALLOW_ADD_WIFI_CONFIG,
+            DISALLOW_CELLULAR_2G,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UserRestrictionKey {}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cd2bbeb..897b7c3 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3374,9 +3374,26 @@
                     }
                 }
 
-                // Fetch all flags for the namespace at once for caching purposes
-                Bundle b = cp.call(cr.getAttributionSource(),
-                        mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+                Bundle b;
+                // b/252663068: if we're in system server and the caller did not call
+                // clearCallingIdentity, the read would fail due to mismatched AttributionSources.
+                // TODO(b/256013480): remove this bypass after fixing the callers in system server.
+                if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER)
+                        && Settings.isInSystemServer()
+                        && Binder.getCallingUid() != Process.myUid()) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        // Fetch all flags for the namespace at once for caching purposes
+                        b = cp.call(cr.getAttributionSource(),
+                                mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                } else {
+                    // Fetch all flags for the namespace at once for caching purposes
+                    b = cp.call(cr.getAttributionSource(),
+                            mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+                }
                 if (b == null) {
                     // Invalid response, return an empty map
                     return keyValues;
@@ -7135,7 +7152,7 @@
          * Format like "ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0"
          * where imeId is ComponentName and subtype is int32.
          */
-        @Readable
+        @Readable(maxTargetSdk = Build.VERSION_CODES.TIRAMISU)
         public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
 
         /**
@@ -7144,7 +7161,7 @@
          * by ':'.
          * @hide
          */
-        @Readable
+        @Readable(maxTargetSdk = Build.VERSION_CODES.TIRAMISU)
         public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods";
 
         /**
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
index 4cc43a1..1d4ac25 100644
--- a/core/java/android/service/credentials/CredentialEntry.java
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -140,8 +140,8 @@
     public static final class Builder {
         private String mType;
         private Slice mSlice;
-        private PendingIntent mPendingIntent;
-        private Credential mCredential;
+        private PendingIntent mPendingIntent = null;
+        private Credential mCredential = null;
         private boolean mAutoSelectAllowed = false;
 
         /**
@@ -172,9 +172,11 @@
          * {@code credential}, or the {@code pendingIntent}.
          */
         public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
-            Preconditions.checkState(pendingIntent != null && mCredential != null,
-                    "credential is already set. Cannot set both the pendingIntent "
-                            + "and the credential");
+            if (pendingIntent != null) {
+                Preconditions.checkState(mCredential != null,
+                        "credential is already set. Cannot set both the pendingIntent "
+                                + "and the credential");
+            }
             mPendingIntent = pendingIntent;
             return this;
         }
@@ -186,9 +188,11 @@
          * the {@code pendingIntent}, or the {@code credential}.
          */
         public @NonNull Builder setCredential(@Nullable Credential credential) {
-            Preconditions.checkState(credential != null && mPendingIntent != null,
-                    "pendingIntent is already set. Cannot set both the "
-                            + "pendingIntent and the credential");
+            if (credential != null) {
+                Preconditions.checkState(mPendingIntent != null,
+                        "pendingIntent is already set. Cannot set both the "
+                                + "pendingIntent and the credential");
+            }
             mCredential = credential;
             return this;
         }
diff --git a/core/java/android/service/credentials/CredentialProviderException.java b/core/java/android/service/credentials/CredentialProviderException.java
index b39b4a0..06f0052 100644
--- a/core/java/android/service/credentials/CredentialProviderException.java
+++ b/core/java/android/service/credentials/CredentialProviderException.java
@@ -30,6 +30,22 @@
 public class CredentialProviderException extends Exception {
     public static final int ERROR_UNKNOWN = 0;
 
+    /**
+     * For internal use only.
+     * Error code to be used when the provider request times out.
+     *
+     * @hide
+     */
+    public static final int ERROR_TIMEOUT = 1;
+
+    /**
+     * For internal use only.
+     * Error code to be used when the async task is canceled internally.
+     *
+     * @hide
+     */
+    public static final int ERROR_TASK_CANCELED = 2;
+
     private final int mErrorCode;
 
     /**
@@ -37,6 +53,8 @@
      */
     @IntDef(prefix = {"ERROR_"}, value = {
             ERROR_UNKNOWN,
+            ERROR_TIMEOUT,
+            ERROR_TASK_CANCELED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CredentialProviderError { }
diff --git a/core/java/android/service/credentials/CredentialProviderInfo.java b/core/java/android/service/credentials/CredentialProviderInfo.java
index e3f8cb7..2c7a983 100644
--- a/core/java/android/service/credentials/CredentialProviderInfo.java
+++ b/core/java/android/service/credentials/CredentialProviderInfo.java
@@ -18,16 +18,21 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.RemoteException;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 
@@ -48,19 +53,21 @@
     @NonNull
     private final List<String> mCapabilities;
 
-    // TODO: Move the two strings below to CredentialProviderService when ready.
-    private static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities";
-    private static final String SERVICE_INTERFACE =
-            "android.service.credentials.CredentialProviderService";
-
+    @NonNull
+    private final Context mContext;
+    @Nullable
+    private final Drawable mIcon;
+    @Nullable
+    private final CharSequence mLabel;
 
     /**
      * Constructs an information instance of the credential provider.
      *
-     * @param context The context object
-     * @param serviceComponent The serviceComponent of the provider service
-     * @param userId The android userId for which the current process is running
+     * @param context the context object
+     * @param serviceComponent the serviceComponent of the provider service
+     * @param userId the android userId for which the current process is running
      * @throws PackageManager.NameNotFoundException If provider service is not found
+     * @throws SecurityException If provider does not require the relevant permission
      */
     public CredentialProviderInfo(@NonNull Context context,
             @NonNull ComponentName serviceComponent, int userId)
@@ -68,7 +75,13 @@
         this(context, getServiceInfoOrThrow(serviceComponent, userId));
     }
 
-    private CredentialProviderInfo(@NonNull Context context, @NonNull ServiceInfo serviceInfo) {
+    /**
+     * Constructs an information instance of the credential provider.
+     * @param context the context object
+     * @param serviceInfo the service info for the provider app. This must be retrieved from the
+     *                    {@code PackageManager}
+     */
+    public CredentialProviderInfo(@NonNull Context context, @NonNull ServiceInfo serviceInfo) {
         if (!Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE.equals(serviceInfo.permission)) {
             Log.i(TAG, "Credential Provider Service from : " + serviceInfo.packageName
                     + "does not require permission"
@@ -76,32 +89,43 @@
             throw new SecurityException("Service does not require the expected permission : "
                     + Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE);
         }
+        mContext = context;
         mServiceInfo = serviceInfo;
         mCapabilities = new ArrayList<>();
-        populateProviderCapabilities(context);
+        mIcon = mServiceInfo.loadIcon(mContext.getPackageManager());
+        mLabel = mServiceInfo.loadSafeLabel(
+                mContext.getPackageManager(), 0 /* do not ellipsize */,
+                TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
+        populateProviderCapabilities(context, serviceInfo);
     }
 
-    private void populateProviderCapabilities(@NonNull Context context) {
-        if (mServiceInfo.applicationInfo.metaData == null) {
-            return;
-        }
+    private void populateProviderCapabilities(@NonNull Context context, ServiceInfo serviceInfo) {
+        final PackageManager pm = context.getPackageManager();
         try {
-            final int resourceId = mServiceInfo.applicationInfo.metaData.getInt(
-                    CAPABILITY_META_DATA_KEY);
-            String[] capabilities = context.getResources().getStringArray(resourceId);
-            if (capabilities == null) {
-                Log.w(TAG, "No capabilities found for provider: " + mServiceInfo.packageName);
+            Bundle metadata = serviceInfo.metaData;
+            Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+            if (metadata == null || resources == null) {
+                Log.i(TAG, "populateProviderCapabilities - metadata or resources is null");
                 return;
             }
+
+            String[] capabilities = resources.getStringArray(metadata.getInt(
+                    CredentialProviderService.CAPABILITY_META_DATA_KEY));
+            if (capabilities == null || capabilities.length == 0) {
+                Slog.i(TAG, "No capabilities found for provider:" + serviceInfo.packageName);
+                return;
+            }
+
             for (String capability : capabilities) {
                 if (capability.isEmpty()) {
-                    Log.w(TAG, "Skipping empty capability");
+                    Slog.i(TAG, "Skipping empty capability");
                     continue;
                 }
+                Slog.i(TAG, "Capabilities found for provider: " + capability);
                 mCapabilities.add(capability);
             }
-        } catch (Resources.NotFoundException e) {
-            Log.w(TAG, "Exception while populating provider capabilities: " + e.getMessage());
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.i(TAG, e.getMessage());
         }
     }
 
@@ -135,6 +159,18 @@
         return mServiceInfo;
     }
 
+    /** Returns the service icon. */
+    @Nullable
+    public Drawable getServiceIcon() {
+        return mIcon;
+    }
+
+    /** Returns the service label. */
+    @Nullable
+    public CharSequence getServiceLabel() {
+        return mLabel;
+    }
+
     /** Returns an immutable list of capabilities this provider service can support. */
     @NonNull
     public List<String> getCapabilities() {
@@ -145,14 +181,15 @@
      * Returns the valid credential provider services available for the user with the
      * given {@code userId}.
      */
+    @NonNull
     public static List<CredentialProviderInfo> getAvailableServices(@NonNull Context context,
             @UserIdInt int userId) {
         final List<CredentialProviderInfo> services = new ArrayList<>();
 
         final List<ResolveInfo> resolveInfos =
                 context.getPackageManager().queryIntentServicesAsUser(
-                        new Intent(SERVICE_INTERFACE),
-                        PackageManager.GET_META_DATA,
+                        new Intent(CredentialProviderService.SERVICE_INTERFACE),
+                        PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA),
                         userId);
         for (ResolveInfo resolveInfo : resolveInfos) {
             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
@@ -169,8 +206,9 @@
      * Returns the valid credential provider services available for the user, that can
      * support the given {@code credentialType}.
      */
+    @NonNull
     public static List<CredentialProviderInfo> getAvailableServicesForCapability(
-            Context context, @UserIdInt int userId, String credentialType) {
+            @NonNull Context context, @UserIdInt int userId, @NonNull String credentialType) {
         List<CredentialProviderInfo> servicesForCapability = new ArrayList<>();
         final List<CredentialProviderInfo> services = getAvailableServices(context, userId);
 
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index 1cdf186..b1b08f4 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -42,6 +42,9 @@
  */
 public abstract class CredentialProviderService extends Service {
     private static final String TAG = "CredProviderService";
+
+    public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities";
+
     private Handler mHandler;
 
     /**
@@ -71,12 +74,13 @@
 
     private final ICredentialProviderService mInterface = new ICredentialProviderService.Stub() {
         @Override
-        public void onGetCredentials(GetCredentialsRequest request, ICancellationSignal transport,
+        public ICancellationSignal onGetCredentials(GetCredentialsRequest request,
                 IGetCredentialsCallback callback) {
             Objects.requireNonNull(request);
-            Objects.requireNonNull(transport);
             Objects.requireNonNull(callback);
 
+            ICancellationSignal transport = CancellationSignal.createTransport();
+
             mHandler.sendMessage(obtainMessage(
                     CredentialProviderService::onGetCredentials,
                     CredentialProviderService.this, request,
@@ -100,15 +104,17 @@
                         }
                     }
             ));
+            return transport;
         }
 
         @Override
-        public void onCreateCredential(CreateCredentialRequest request,
-                ICancellationSignal transport, ICreateCredentialCallback callback) {
+        public ICancellationSignal onCreateCredential(CreateCredentialRequest request,
+                ICreateCredentialCallback callback) {
             Objects.requireNonNull(request);
-            Objects.requireNonNull(transport);
             Objects.requireNonNull(callback);
 
+            ICancellationSignal transport = CancellationSignal.createTransport();
+
             mHandler.sendMessage(obtainMessage(
                     CredentialProviderService::onCreateCredential,
                     CredentialProviderService.this, request,
@@ -132,6 +138,7 @@
                         }
                     }
             ));
+            return transport;
         }
     };
 
diff --git a/core/java/android/service/credentials/ICredentialProviderService.aidl b/core/java/android/service/credentials/ICredentialProviderService.aidl
index c68430c..c21cefa 100644
--- a/core/java/android/service/credentials/ICredentialProviderService.aidl
+++ b/core/java/android/service/credentials/ICredentialProviderService.aidl
@@ -21,13 +21,14 @@
 import android.service.credentials.CreateCredentialRequest;
 import android.service.credentials.IGetCredentialsCallback;
 import android.service.credentials.ICreateCredentialCallback;
+import android.os.ICancellationSignal;
 
 /**
  * Interface from the system to a credential provider service.
  *
  * @hide
  */
-oneway interface ICredentialProviderService {
-    void onGetCredentials(in GetCredentialsRequest request, in ICancellationSignal transport, in IGetCredentialsCallback callback);
-    void onCreateCredential(in CreateCredentialRequest request, in ICancellationSignal transport, in ICreateCredentialCallback callback);
+interface ICredentialProviderService {
+    ICancellationSignal onGetCredentials(in GetCredentialsRequest request, in IGetCredentialsCallback callback);
+    ICancellationSignal onCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback);
 }
diff --git a/core/java/android/service/credentials/SaveEntry.java b/core/java/android/service/credentials/SaveEntry.java
index abe51d4..55ff6ff 100644
--- a/core/java/android/service/credentials/SaveEntry.java
+++ b/core/java/android/service/credentials/SaveEntry.java
@@ -17,17 +17,11 @@
 package android.service.credentials;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.app.slice.Slice;
-import android.credentials.Credential;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.Preconditions;
-
-import java.util.Objects;
-
 /**
  * An entry to be shown on the UI. This entry represents where the credential to be created will
  * be stored. Examples include user's account, family group etc.
@@ -36,13 +30,11 @@
  */
 public final class SaveEntry implements Parcelable {
     private final @NonNull Slice mSlice;
-    private final @Nullable PendingIntent mPendingIntent;
-    private final @Nullable Credential mCredential;
+    private final @NonNull PendingIntent mPendingIntent;
 
     private SaveEntry(@NonNull Parcel in) {
         mSlice = in.readTypedObject(Slice.CREATOR);
         mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
-        mCredential = in.readTypedObject(Credential.CREATOR);
     }
 
     public static final @NonNull Creator<SaveEntry> CREATOR = new Creator<SaveEntry>() {
@@ -66,18 +58,23 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeTypedObject(mSlice, flags);
         dest.writeTypedObject(mPendingIntent, flags);
-        dest.writeTypedObject(mCredential, flags);
     }
 
-    /* package-private */ SaveEntry(
+    /**
+     * Constructs a save entry to be displayed on the UI.
+     *
+     * @param slice the display content to be displayed on the UI, along with this entry
+     * @param pendingIntent the intent to be invoked when the user selects this entry
+     */
+    public SaveEntry(
             @NonNull Slice slice,
-            @Nullable PendingIntent pendingIntent,
-            @Nullable Credential credential) {
+            @NonNull PendingIntent pendingIntent) {
         this.mSlice = slice;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mSlice);
         this.mPendingIntent = pendingIntent;
-        this.mCredential = credential;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPendingIntent);
     }
 
     /** Returns the content to be displayed with this save entry on the UI. */
@@ -86,76 +83,7 @@
     }
 
     /** Returns the pendingIntent to be invoked when this save entry on the UI is selectcd. */
-    public @Nullable PendingIntent getPendingIntent() {
+    public @NonNull PendingIntent getPendingIntent() {
         return mPendingIntent;
     }
-
-    /** Returns the credential produced by the {@link CreateCredentialRequest}. */
-    public @Nullable Credential getCredential() {
-        return mCredential;
-    }
-
-    /**
-     * A builder for {@link SaveEntry}.
-     */
-    public static final class Builder {
-
-        private @NonNull Slice mSlice;
-        private @Nullable PendingIntent mPendingIntent;
-        private @Nullable Credential mCredential;
-
-        /**
-         * Builds the instance.
-         * @param slice the content to be displayed with this save entry
-         *
-         * @throws NullPointerException If {@code slice} is null.
-         */
-        public Builder(@NonNull Slice slice) {
-            mSlice = Objects.requireNonNull(slice, "slice must not be null");
-        }
-
-        /**
-         * Sets the pendingIntent to be invoked when this entry is selected by the user.
-         *
-         * @throws IllegalStateException If {@code credential} is already set. Must only set either
-         * {@code credential}, or the {@code pendingIntent}.
-         */
-        public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
-            Preconditions.checkState(pendingIntent != null
-                    && mCredential != null, "credential is already set. Must only set "
-                    + "either the pendingIntent or the credential");
-            mPendingIntent = pendingIntent;
-            return this;
-        }
-
-        /**
-         * Sets the credential to be returned when this entry is selected by the user.
-         *
-         * @throws IllegalStateException If {@code pendingIntent} is already set. Must only
-         * set either the {@code pendingIntent}, or {@code credential}.
-         */
-        public @NonNull Builder setCredential(@Nullable Credential credential) {
-            Preconditions.checkState(credential != null && mPendingIntent != null,
-                    "pendingIntent is already set. Must only set either the credential "
-                            + "or the pendingIntent");
-            mCredential = credential;
-            return this;
-        }
-
-        /**
-         * Builds the instance.
-         *
-         * @throws IllegalStateException if both {@code pendingIntent} and {@code credential}
-         * are null.
-         */
-        public @NonNull SaveEntry build() {
-            Preconditions.checkState(mPendingIntent == null && mCredential == null,
-                    "pendingIntent and credential both must not be null. Must set "
-                            + "either the pendingIntnet or the credential");
-            return new SaveEntry(
-                    mSlice,
-                    mPendingIntent,
-                    mCredential);
-        }
-    }
 }
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index 295171c..5f30ad0 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -54,6 +54,13 @@
     public abstract void requestDream();
 
     /**
+     * Whether dreaming can start given user settings and the current dock/charge state.
+     *
+     * @param isScreenOn True if the screen is currently on.
+     */
+    public abstract boolean canStartDreaming(boolean isScreenOn);
+
+    /**
      * Called by the ActivityTaskManagerService to verify that the startDreamActivity
      * request comes from the current active dream component.
      *
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index c6cd708..37fc9f2 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -575,6 +575,7 @@
          */
         public void reportEngineShown(boolean waitForEngineShown) {
             if (mIWallpaperEngine.mShownReported) return;
+            Log.d(TAG, "reportEngineShown: shouldWait=" + waitForEngineShown);
             if (!waitForEngineShown) {
                 Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN);
                 mCaller.removeMessages(MSG_REPORT_SHOWN);
diff --git a/core/java/android/text/GraphemeClusterSegmentFinder.java b/core/java/android/text/GraphemeClusterSegmentFinder.java
index 3335751..656774f 100644
--- a/core/java/android/text/GraphemeClusterSegmentFinder.java
+++ b/core/java/android/text/GraphemeClusterSegmentFinder.java
@@ -49,6 +49,7 @@
 
     @Override
     public int previousStartBoundary(@IntRange(from = 0) int offset) {
+        if (offset == 0) return DONE;
         int boundary = mTextPaint.getTextRunCursor(
                 mText, 0, mText.length(), false, offset, Paint.CURSOR_BEFORE);
         return boundary == -1 ? DONE : boundary;
@@ -56,6 +57,7 @@
 
     @Override
     public int previousEndBoundary(@IntRange(from = 0) int offset) {
+        if (offset == 0) return DONE;
         int boundary = mTextPaint.getTextRunCursor(
                 mText, 0, mText.length(), false, offset, Paint.CURSOR_BEFORE);
         // Check that there is another cursor position before, otherwise this is not a valid
@@ -69,6 +71,7 @@
 
     @Override
     public int nextStartBoundary(@IntRange(from = 0) int offset) {
+        if (offset == mText.length()) return DONE;
         int boundary = mTextPaint.getTextRunCursor(
                 mText, 0, mText.length(), false, offset, Paint.CURSOR_AFTER);
         // Check that there is another cursor position after, otherwise this is not a valid
@@ -82,6 +85,7 @@
 
     @Override
     public int nextEndBoundary(@IntRange(from = 0) int offset) {
+        if (offset == mText.length()) return DONE;
         int boundary = mTextPaint.getTextRunCursor(
                 mText, 0, mText.length(), false, offset, Paint.CURSOR_AFTER);
         return boundary == -1 ? DONE : boundary;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 1337d6a..54ec07e 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -3000,6 +3000,18 @@
         }
 
         /**
+         * Returns the BiDi level of this run.
+         *
+         * @param runIndex the index of the BiDi run
+         * @return the BiDi level of this run.
+         * @hide
+         */
+        @IntRange(from = 0)
+        public int getRunLevel(int runIndex) {
+            return (mDirections[runIndex * 2 + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK;
+        }
+
+        /**
          * Returns true if the BiDi run is RTL.
          *
          * @param runIndex the index of the BiDi run
diff --git a/core/java/android/text/SegmentFinder.java b/core/java/android/text/SegmentFinder.java
index c21c577..be0094b 100644
--- a/core/java/android/text/SegmentFinder.java
+++ b/core/java/android/text/SegmentFinder.java
@@ -19,6 +19,13 @@
 import android.annotation.IntRange;
 import android.graphics.RectF;
 
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
  * Finds text segment boundaries within text. Subclasses can implement different types of text
  * segments. Grapheme clusters and words are examples of possible text segments. These are
@@ -63,4 +70,144 @@
      * character offset, or {@code DONE} if there are none.
      */
     public abstract int nextEndBoundary(@IntRange(from = 0) int offset);
+
+    /**
+     * The default {@link SegmentFinder} implementation based on given segment ranges.
+     */
+    public static class DefaultSegmentFinder extends SegmentFinder {
+        private final int[] mSegments;
+
+        /**
+         * Create a SegmentFinder with segments stored in an array, where i-th segment's start is
+         * stored at segments[2 * i] and end is stored at segments[2 * i + 1] respectively.
+         *
+         * <p> It is required that segments do not overlap, and are already sorted by their start
+         * indices. </p>
+         * @param segments the array that stores the segment ranges.
+         * @throws IllegalArgumentException if the given segments array's length is not even; the
+         * given segments are not sorted or there are segments overlap with others.
+         */
+        public DefaultSegmentFinder(@NonNull int[] segments) {
+            checkSegmentsValid(segments);
+            mSegments = segments;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int previousStartBoundary(@IntRange(from = 0) int offset) {
+            return findPrevious(offset, /* isStart = */ true);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int previousEndBoundary(@IntRange(from = 0) int offset) {
+            return findPrevious(offset, /* isStart = */ false);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int nextStartBoundary(@IntRange(from = 0) int offset) {
+            return findNext(offset, /* isStart = */ true);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int nextEndBoundary(@IntRange(from = 0) int offset) {
+            return findNext(offset, /* isStart = */ false);
+        }
+
+        private int findNext(int offset, boolean isStart) {
+            if (offset < 0) return DONE;
+            if (mSegments.length < 1 || offset > mSegments[mSegments.length - 1]) return DONE;
+
+            if (offset < mSegments[0]) {
+                return isStart ? mSegments[0] : mSegments[1];
+            }
+
+            int index = Arrays.binarySearch(mSegments, offset);
+            if (index >= 0) {
+                // mSegments may have duplicate elements (The previous segments end equals
+                // to the following segments start.) Move the index forwards since we are searching
+                // for the next segment.
+                if (index + 1 < mSegments.length && mSegments[index + 1] == offset) {
+                    index = index + 1;
+                }
+                // Point the index to the first segment boundary larger than the given offset.
+                index += 1;
+            } else {
+                // binarySearch returns the insertion point, it's the first segment boundary larger
+                // than the given offset.
+                index = -(index + 1);
+            }
+            if (index >= mSegments.length) return DONE;
+
+            //  +---------------------------------------+
+            //  |               | isStart   | isEnd     |
+            //  |---------------+-----------+-----------|
+            //  | indexIsStart  | index     | index + 1 |
+            //  |---------------+-----------+-----------|
+            //  | indexIsEnd    | index + 1 | index     |
+            //  +---------------------------------------+
+            boolean indexIsStart = index % 2 == 0;
+            if (isStart != indexIsStart) {
+                return (index + 1 < mSegments.length) ? mSegments[index + 1] : DONE;
+            }
+            return mSegments[index];
+        }
+
+        private int findPrevious(int offset, boolean isStart) {
+            if (mSegments.length < 1 || offset < mSegments[0]) return DONE;
+
+            if (offset > mSegments[mSegments.length - 1]) {
+                return isStart ? mSegments[mSegments.length - 2] : mSegments[mSegments.length - 1];
+            }
+
+            int index = Arrays.binarySearch(mSegments, offset);
+            if (index >= 0) {
+                // mSegments may have duplicate elements (when the previous segments end equal
+                // to the following segments start). Move the index backwards since we are searching
+                // for the previous segment.
+                if (index > 0 && mSegments[index - 1] == offset) {
+                    index = index - 1;
+                }
+                // Point the index to the first segment boundary smaller than the given offset.
+                index -= 1;
+            } else {
+                // binarySearch returns the insertion point, insertionPoint - 1 is the first
+                // segment boundary smaller than the given offset.
+                index = -(index + 1) - 1;
+            }
+            if (index < 0) return DONE;
+
+            //  +---------------------------------------+
+            //  |               | isStart   | isEnd     |
+            //  |---------------+-----------+-----------|
+            //  | indexIsStart  | index     | index - 1 |
+            //  |---------------+-----------+-----------|
+            //  | indexIsEnd    | index - 1 | index     |
+            //  +---------------------------------------+
+            boolean indexIsStart = index % 2 == 0;
+            if (isStart != indexIsStart) {
+                return (index > 0) ? mSegments[index - 1] : DONE;
+            }
+            return mSegments[index];
+        }
+
+        private static void checkSegmentsValid(int[] segments) {
+            Objects.requireNonNull(segments);
+            Preconditions.checkArgument(segments.length % 2 == 0,
+                    "the length of segments must be even");
+            if (segments.length == 0) return;
+            int lastSegmentEnd = Integer.MIN_VALUE;
+            for (int index = 0; index < segments.length; index += 2) {
+                if (segments[index] < lastSegmentEnd) {
+                    throw new IllegalArgumentException("segments can't overlap");
+                }
+                if (segments[index] >= segments[index + 1]) {
+                    throw new IllegalArgumentException("the segment range can't be empty");
+                }
+                lastSegmentEnd = segments[index + 1];
+            }
+        }
+    }
 }
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 596e491..ff66e5f 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -2331,7 +2331,8 @@
         return trimmed;
     }
 
-    private static boolean isNewline(int codePoint) {
+    /** @hide */
+    public static boolean isNewline(int codePoint) {
         int type = Character.getType(codePoint);
         return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR
                 || codePoint == LINE_FEED_CODE_POINT;
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index d4bcd12..01989d5 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -229,6 +229,8 @@
                         break;
                     } else if (Emoji.isEmojiModifierBase(codePoint)) {
                         deleteCharCount += Character.charCount(codePoint);
+                        state = STATE_BEFORE_EMOJI;
+                        break;
                     }
                     state = STATE_FINISHED;
                     break;
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 0a3e6b1..517d982 100755
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -174,6 +174,14 @@
      * This is not a density that applications should target, instead relying
      * on the system to scale their {@link #DENSITY_XXXHIGH} assets for them.
      */
+    public static final int DENSITY_520 = 520;
+
+    /**
+     * Intermediate density for screens that sit somewhere between
+     * {@link #DENSITY_XXHIGH} (480 dpi) and {@link #DENSITY_XXXHIGH} (640 dpi).
+     * This is not a density that applications should target, instead relying
+     * on the system to scale their {@link #DENSITY_XXXHIGH} assets for them.
+     */
     public static final int DENSITY_560 = 560;
 
     /**
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index d1f05ec..7b6a6d2 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -103,6 +103,25 @@
     public static final String SETTINGS_NEW_KEYBOARD_UI = "settings_new_keyboard_ui";
 
     /**
+     * Enable new shortcut list UI
+     * @hide
+     */
+    public static final String SETTINGS_NEW_KEYBOARD_SHORTCUT = "settings_new_keyboard_shortcut";
+
+    /**
+     * Enable new modifier key settings UI
+     * @hide
+     */
+    public static final String SETTINGS_NEW_KEYBOARD_MODIFIER_KEY =
+            "settings_new_keyboard_modifier_key";
+
+    /**
+     * Enable new trackpad settings UI
+     * @hide
+     */
+    public static final String SETTINGS_NEW_KEYBOARD_TRACKPAD = "settings_new_keyboard_trackpad";
+
+    /**
      * Enable the new pages which is implemented with SPA.
      * @hide
      */
@@ -143,6 +162,9 @@
         DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true");
         DEFAULT_FLAGS.put(SETTINGS_AUTO_TEXT_WRAPPING, "false");
         DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_UI, "false");
+        DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_SHORTCUT, "false");
+        DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY, "false");
+        DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD, "false");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA, "false");
         DEFAULT_FLAGS.put(SETTINGS_ADB_METRICS_WRITER, "false");
     }
@@ -158,6 +180,9 @@
         PERSISTENT_FLAGS.add(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE);
         PERSISTENT_FLAGS.add(SETTINGS_AUTO_TEXT_WRAPPING);
         PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_UI);
+        PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_SHORTCUT);
+        PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY);
+        PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_TRACKPAD);
     }
 
     /**
diff --git a/core/java/android/view/HandwritingDelegateConfiguration.java b/core/java/android/view/HandwritingDelegateConfiguration.java
new file mode 100644
index 0000000..524bb4c
--- /dev/null
+++ b/core/java/android/view/HandwritingDelegateConfiguration.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.view;
+
+import android.annotation.IdRes;
+import android.annotation.NonNull;
+
+/**
+ * Configuration for a view to act as a handwriting initiation delegate. This allows handwriting
+ * mode for a delegator editor view to be initiated by stylus movement on the delegate view.
+ *
+ * <p>If a stylus {@link MotionEvent} occurs within the delegate view's bounds, the callback
+ * returned by {@link #getInitiationCallback()} will be called. The callback implementation is
+ * expected to show and focus the delegator editor view. If a view with identifier matching {@link
+ * #getDelegatorViewId()} creates an input connection while the same stylus {@link MotionEvent}
+ * sequence is ongoing, handwriting mode will be initiated for that view.
+ *
+ * <p>A common use case is a custom view which looks like a text editor but does not actually
+ * support text editing itself, and clicking on the custom view causes an EditText to be shown. To
+ * support handwriting initiation in this case, {@link View#setHandwritingDelegateConfiguration} can
+ * be called on the custom view to configure it as a delegate, and set the EditText as the delegator
+ * by passing the EditText's identifier as the {@code delegatorViewId}. The {@code
+ * initiationCallback} implementation is typically the same as the click listener implementation
+ * which shows the EditText.
+ */
+public class HandwritingDelegateConfiguration {
+    @IdRes private final int mDelegatorViewId;
+    @NonNull private final Runnable mInitiationCallback;
+
+    /**
+     * Constructs a HandwritingDelegateConfiguration instance.
+     *
+     * @param delegatorViewId identifier of the delegator editor view for which handwriting mode
+     *     should be initiated
+     * @param initiationCallback callback called when a stylus {@link MotionEvent} occurs within
+     *     this view's bounds
+     */
+    public HandwritingDelegateConfiguration(
+            @IdRes int delegatorViewId, @NonNull Runnable initiationCallback) {
+        mDelegatorViewId = delegatorViewId;
+        mInitiationCallback = initiationCallback;
+    }
+
+    /**
+     * Returns the identifier of the delegator editor view for which handwriting mode should be
+     * initiated.
+     */
+    public int getDelegatorViewId() {
+        return mDelegatorViewId;
+    }
+
+    /**
+     * Returns the callback which should be called when a stylus {@link MotionEvent} occurs within
+     * the delegate view's bounds.
+     */
+    @NonNull
+    public Runnable getInitiationCallback() {
+        return mInitiationCallback;
+    }
+}
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index a0a07b3..2e4073e 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.IdRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Rect;
@@ -161,6 +162,15 @@
                     if (candidateView != null) {
                         if (candidateView == getConnectedView()) {
                             startHandwriting(candidateView);
+                        } else if (candidateView.getHandwritingDelegateConfiguration() != null) {
+                            mState.mDelegatorViewId =
+                                    candidateView
+                                            .getHandwritingDelegateConfiguration()
+                                            .getDelegatorViewId();
+                            candidateView
+                                    .getHandwritingDelegateConfiguration()
+                                    .getInitiationCallback()
+                                    .run();
                         } else {
                             if (candidateView.getRevealOnFocusHint()) {
                                 candidateView.setRevealOnFocusHint(false);
@@ -259,8 +269,10 @@
         }
 
         final Rect handwritingArea = getViewHandwritingArea(connectedView);
-        if (isInHandwritingArea(handwritingArea, mState.mStylusDownX,
-                mState.mStylusDownY, connectedView)) {
+        if ((mState.mDelegatorViewId != View.NO_ID
+                        && mState.mDelegatorViewId == connectedView.getId())
+                || isInHandwritingArea(
+                        handwritingArea, mState.mStylusDownX, mState.mStylusDownY, connectedView)) {
             startHandwriting(connectedView);
         } else {
             mState.mShouldInitHandwriting = false;
@@ -287,6 +299,11 @@
         if (!view.isAutoHandwritingEnabled()) {
             return false;
         }
+        // The view may be a handwriting initiation delegate, in which case it is not the editor
+        // view for which handwriting would be started. However, in almost all cases, the return
+        // values of View#isStylusHandwritingAvailable will be the same for the delegate view and
+        // the delegator editor view. So the delegate view can be used to decide whether handwriting
+        // should be triggered.
         return view.isStylusHandwritingAvailable();
     }
 
@@ -473,6 +490,13 @@
          * built InputConnection.
          */
         private boolean mExceedHandwritingSlop;
+        /**
+         * If the current ongoing stylus MotionEvent sequence started over a handwriting initiation
+         * delegate view, then this is the view identifier of the corresponding delegator view. If
+         * the delegator view creates an input connection while the MotionEvent sequence is still
+         * ongoing, then handwriting mode will be initiated for the delegator view.
+         */
+        @IdRes private int mDelegatorViewId = View.NO_ID;
 
         /** The pointer id of the stylus pointer that is being tracked. */
         private final int mStylusPointerId;
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 9b1d867..799955b 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1010,6 +1011,22 @@
     }
 
     /**
+     * Returns the Bluetooth address of this input device, if known.
+     *
+     * The returned string is always null if this input device is not connected
+     * via Bluetooth, or if the Bluetooth address of the device cannot be
+     * determined. The returned address will look like: "11:22:33:44:55:66".
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @Nullable
+    public String getBluetoothAddress() {
+        // We query the address via a separate InputManager API instead of pre-populating it in
+        // this class to avoid leaking it to apps that do not have sufficient permissions.
+        return InputManager.getInstance().getInputDeviceBluetoothAddress(mId);
+    }
+
+    /**
      * Gets the vibrator service associated with the device, if there is one.
      * Even if the device does not have a vibrator, the result is never null.
      * Use {@link Vibrator#hasVibrator} to determine whether a vibrator is
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 198ac9d..b24303b 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -121,16 +121,23 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_POSITION = false;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(
+            maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Track {@link SurfaceHolder#addCallback} instead")
     final ArrayList<SurfaceHolder.Callback> mCallbacks = new ArrayList<>();
 
     final int[] mLocation = new int[2];
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(
+            maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@link SurfaceHolder#lockCanvas} instead")
     final ReentrantLock mSurfaceLock = new ReentrantLock();
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(
+            maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@link SurfaceHolder#getSurface} instead")
     final Surface mSurface = new Surface();       // Current surface in use
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023,
+                         publicAlternatives = "Use {@link View#getVisibility} instead")
     boolean mDrawingStopped = true;
     // We use this to track if the application has produced a frame
     // in to the Surface. Up until that point, we should be careful not to punch
@@ -156,13 +163,16 @@
     int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
     int mRequestedSubLayer = APPLICATION_MEDIA_SUBLAYER;
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023,
+                         publicAlternatives = "Use {@link SurfaceHolder#isCreating} instead")
     boolean mIsCreating = false;
 
     private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener =
             this::updateSurface;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(
+            maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Rely on {@link ViewTreeObserver#dispatchOnPreDraw} instead")
     private final ViewTreeObserver.OnPreDrawListener mDrawListener = () -> {
         // reposition ourselves where the surface is
         mHaveFrame = getWidth() > 0 && getHeight() > 0;
@@ -176,24 +186,32 @@
     boolean mViewVisibility = false;
     boolean mWindowStopped = false;
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023,
+                         publicAlternatives = "Use {@link View#getWidth} instead")
     int mRequestedWidth = -1;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023,
+                         publicAlternatives = "Use {@link View#getHeight} instead")
     int mRequestedHeight = -1;
     /* Set SurfaceView's format to 565 by default to maintain backward
      * compatibility with applications assuming this format.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(
+            maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@code SurfaceHolder.Callback#surfaceChanged} instead")
     int mRequestedFormat = PixelFormat.RGB_565;
 
     float mAlpha = 1f;
     boolean mClipSurfaceToBounds;
     int mBackgroundColor = Color.BLACK;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(
+            maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@link View#getWidth} and {@link View#getHeight} to "
+                    + "determine if the SurfaceView is onscreen and has a frame")
     boolean mHaveFrame = false;
     boolean mSurfaceCreated = false;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023,
+                         publicAlternatives = "Time {@link SurfaceHolder#lockCanvas} instead")
     long mLastLockTime = 0;
 
     boolean mVisible = false;
@@ -202,9 +220,13 @@
     int mSurfaceWidth = -1;
     int mSurfaceHeight = -1;
     float mCornerRadius;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(
+            maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@code SurfaceHolder.Callback#surfaceChanged} "
+            + "instead")
     int mFormat = -1;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023,
+                         publicAlternatives = "Use {@link SurfaceHolder#getSurfaceFrame} instead")
     final Rect mSurfaceFrame = new Rect();
     int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
     @SurfaceControl.BufferTransform int mTransformHint = 0;
@@ -1410,7 +1432,9 @@
      * @return true if the surface has dimensions that are fixed in size
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(
+            maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Track {@link SurfaceHolder#setFixedSize} instead")
     public boolean isFixedSize() {
         return (mRequestedWidth != -1 || mRequestedHeight != -1);
     }
@@ -1446,7 +1470,9 @@
         updateBackgroundColor(t);
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(
+            maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@link SurfaceView#getHolder} instead")
     private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
         private static final String LOG_TAG = "SurfaceHolder";
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7b6ebf7..49d9e67 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5063,6 +5063,13 @@
     private boolean mHoveringTouchDelegate = false;
 
     /**
+     * Configuration for this view to act as a handwriting initiation delegate. This allows
+     * handwriting mode for a delegator editor view to be initiated by stylus movement on this
+     * delegate view.
+     */
+    private HandwritingDelegateConfiguration mHandwritingDelegateConfiguration;
+
+    /**
      * Solid color to use as a background when creating the drawing cache. Enables
      * the cache to use 16 bit bitmaps instead of 32 bit.
      */
@@ -12255,6 +12262,30 @@
     }
 
     /**
+     * Configures this view to act as a handwriting initiation delegate. This allows handwriting
+     * mode for a delegator editor view to be initiated by stylus movement on this delegate view.
+     *
+     * <p>If {@code null} is passed, this view will no longer act as a handwriting initiation
+     * delegate.
+     */
+    public void setHandwritingDelegateConfiguration(
+            @Nullable HandwritingDelegateConfiguration configuration) {
+        mHandwritingDelegateConfiguration = configuration;
+        if (configuration != null) {
+            setHandwritingArea(new Rect(0, 0, getWidth(), getHeight()));
+        }
+    }
+
+    /**
+     * If this view has been configured as a handwriting initiation delegate, returns the delegate
+     * configuration.
+     */
+    @Nullable
+    public HandwritingDelegateConfiguration getHandwritingDelegateConfiguration() {
+        return mHandwritingDelegateConfiguration;
+    }
+
+    /**
      * Gets the coordinates of this view in the coordinate space of the
      * {@link Surface} that contains the view.
      *
@@ -24205,7 +24236,7 @@
             }
         }
         rebuildOutline();
-        if (onCheckIsTextEditor()) {
+        if (onCheckIsTextEditor() || mHandwritingDelegateConfiguration != null) {
             setHandwritingArea(new Rect(0, 0, newWidth, newHeight));
         }
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5e836ef..e664ebf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1257,7 +1257,7 @@
                     mTmpFrames.attachedFrame = attachedFrame;
                     mTmpFrames.sizeCompatScale = sizeCompatScale[0];
                     mInvSizeCompatScale = 1f / sizeCompatScale[0];
-                } catch (RemoteException e) {
+                } catch (RemoteException | RuntimeException e) {
                     mAdded = false;
                     mView = null;
                     mAttachInfo.mRootView = null;
@@ -4970,7 +4970,7 @@
     }
 
     void reportKeepClearAreasChanged() {
-        if (!mHasPendingKeepClearAreaChange) {
+        if (!mHasPendingKeepClearAreaChange || mView == null) {
             return;
         }
         mHasPendingKeepClearAreaChange = false;
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
new file mode 100644
index 0000000..aeff37c
--- /dev/null
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -0,0 +1,515 @@
+/*
+ * 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 android.view.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.view.WindowManager;
+import android.window.ImeOnBackInvokedDispatcher;
+
+import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.view.IInputMethodManager;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A global wrapper to directly invoke {@link IInputMethodManager} IPCs.
+ *
+ * <p>All public static methods are guaranteed to be thread-safe.</p>
+ *
+ * <p>All public methods are guaranteed to do nothing when {@link IInputMethodManager} is
+ * unavailable.</p>
+ *
+ * <p>If you want to use any of this method outside of {@code android.view.inputmethod}, create
+ * a wrapper method in {@link InputMethodManagerGlobal} instead of making this class public.</p>
+ */
+final class IInputMethodManagerGlobalInvoker {
+    @Nullable
+    private static volatile IInputMethodManager sServiceCache = null;
+
+    /**
+     * @return {@code true} if {@link IInputMethodManager} is available.
+     */
+    @AnyThread
+    static boolean isAvailable() {
+        return getService() != null;
+    }
+
+    @AnyThread
+    @Nullable
+    static IInputMethodManager getService() {
+        IInputMethodManager service = sServiceCache;
+        if (service == null) {
+            if (InputMethodManager.isInEditModeInternal()) {
+                return null;
+            }
+            service = IInputMethodManager.Stub.asInterface(
+                    ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
+            if (service == null) {
+                return null;
+            }
+            sServiceCache = service;
+        }
+        return service;
+    }
+
+    @AnyThread
+    private static void handleRemoteExceptionOrRethrow(@NonNull RemoteException e,
+            @Nullable Consumer<RemoteException> exceptionHandler) {
+        if (exceptionHandler != null) {
+            exceptionHandler.accept(e);
+        } else {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Invokes {@link IInputMethodManager#startProtoDump(byte[], int, String)}.
+     *
+     * @param protoDump client or service side information to be stored by the server
+     * @param source where the information is coming from, refer to
+     *               {@link com.android.internal.inputmethod.ImeTracing#IME_TRACING_FROM_CLIENT} and
+     *               {@link com.android.internal.inputmethod.ImeTracing#IME_TRACING_FROM_IMS}
+     * @param where where the information is coming from.
+     * @param exceptionHandler an optional {@link RemoteException} handler.
+     */
+    @RequiresNoPermission
+    @AnyThread
+    static void startProtoDump(byte[] protoDump, int source, String where,
+            @Nullable Consumer<RemoteException> exceptionHandler) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.startProtoDump(protoDump, source, where);
+        } catch (RemoteException e) {
+            handleRemoteExceptionOrRethrow(e, exceptionHandler);
+        }
+    }
+
+    /**
+     * Invokes {@link IInputMethodManager#startImeTrace()}.
+     *
+     * @param exceptionHandler an optional {@link RemoteException} handler.
+     */
+    @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
+    @AnyThread
+    static void startImeTrace(@Nullable Consumer<RemoteException> exceptionHandler) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.startImeTrace();
+        } catch (RemoteException e) {
+            handleRemoteExceptionOrRethrow(e, exceptionHandler);
+        }
+    }
+
+    /**
+     * Invokes {@link IInputMethodManager#stopImeTrace()}.
+     *
+     * @param exceptionHandler an optional {@link RemoteException} handler.
+     */
+    @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
+    @AnyThread
+    static void stopImeTrace(@Nullable Consumer<RemoteException> exceptionHandler) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.stopImeTrace();
+        } catch (RemoteException e) {
+            handleRemoteExceptionOrRethrow(e, exceptionHandler);
+        }
+    }
+
+    /**
+     * Invokes {@link IInputMethodManager#isImeTraceEnabled()}.
+     *
+     * @return The return value of {@link IInputMethodManager#isImeTraceEnabled()}.
+     */
+    @RequiresNoPermission
+    @AnyThread
+    static boolean isImeTraceEnabled() {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return false;
+        }
+        try {
+            return service.isImeTraceEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Invokes {@link IInputMethodManager#removeImeSurface()}
+     */
+    @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @AnyThread
+    static void removeImeSurface(@Nullable Consumer<RemoteException> exceptionHandler) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.removeImeSurface();
+        } catch (RemoteException e) {
+            handleRemoteExceptionOrRethrow(e, exceptionHandler);
+        }
+    }
+
+    @AnyThread
+    static void addClient(@NonNull IInputMethodClient client,
+            @NonNull IRemoteInputConnection fallbackInputConnection, int untrustedDisplayId) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.addClient(client, fallbackInputConnection, untrustedDisplayId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    @NonNull
+    static List<InputMethodInfo> getInputMethodList(@UserIdInt int userId,
+            @DirectBootAwareness int directBootAwareness) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return new ArrayList<>();
+        }
+        try {
+            return service.getInputMethodList(userId, directBootAwareness);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    @NonNull
+    static List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return new ArrayList<>();
+        }
+        try {
+            return service.getEnabledInputMethodList(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    @NonNull
+    static List<InputMethodSubtype> getEnabledInputMethodSubtypeList(@Nullable String imiId,
+            boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return new ArrayList<>();
+        }
+        try {
+            return service.getEnabledInputMethodSubtypeList(imiId,
+                    allowsImplicitlyEnabledSubtypes, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    @Nullable
+    static InputMethodSubtype getLastInputMethodSubtype(@UserIdInt int userId) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return null;
+        }
+        try {
+            return service.getLastInputMethodSubtype(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
+            int flags, int lastClickToolType, @Nullable ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return false;
+        }
+        try {
+            return service.showSoftInput(
+                    client, windowToken, flags, lastClickToolType, resultReceiver, reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
+            int flags, @Nullable ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return false;
+        }
+        try {
+            return service.hideSoftInput(client, windowToken, flags, resultReceiver, reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    @NonNull
+    static InputBindResult startInputOrWindowGainedFocus(@StartInputReason int startInputReason,
+            @NonNull IInputMethodClient client, @Nullable IBinder windowToken,
+            @StartInputFlags int startInputFlags,
+            @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+            @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
+            @Nullable IRemoteInputConnection remoteInputConnection,
+            @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return InputBindResult.NULL;
+        }
+        try {
+            return service.startInputOrWindowGainedFocus(startInputReason, client, windowToken,
+                    startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
+                    remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
+                    imeDispatcher);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static void showInputMethodPickerFromClient(@NonNull IInputMethodClient client,
+            int auxiliarySubtypeMode) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.showInputMethodPickerFromClient(client, auxiliarySubtypeMode);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static void showInputMethodPickerFromSystem(@NonNull IInputMethodClient client,
+            int auxiliarySubtypeMode, int displayId) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.showInputMethodPickerFromSystem(client, auxiliarySubtypeMode, displayId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static boolean isInputMethodPickerShownForTest() {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return false;
+        }
+        try {
+            return service.isInputMethodPickerShownForTest();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    @Nullable
+    static InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return null;
+        }
+        try {
+            return service.getCurrentInputMethodSubtype(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static void setAdditionalInputMethodSubtypes(@NonNull String imeId,
+            @NonNull InputMethodSubtype[] subtypes, @UserIdInt int userId) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.setAdditionalInputMethodSubtypes(imeId, subtypes, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static void setExplicitlyEnabledInputMethodSubtypes(@NonNull String imeId,
+            @NonNull int[] subtypeHashCodes, @UserIdInt int userId) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static int getInputMethodWindowVisibleHeight(@NonNull IInputMethodClient client) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return 0;
+        }
+        try {
+            return service.getInputMethodWindowVisibleHeight(client);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static void reportVirtualDisplayGeometryAsync(@NonNull IInputMethodClient client,
+            int childDisplayId, @Nullable float[] matrixValues) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.reportVirtualDisplayGeometryAsync(client, childDisplayId, matrixValues);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static void reportPerceptibleAsync(@NonNull IBinder windowToken, boolean perceptible) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.reportPerceptibleAsync(windowToken, perceptible);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static void removeImeSurfaceFromWindowAsync(@NonNull IBinder windowToken) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.removeImeSurfaceFromWindowAsync(windowToken);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static void startStylusHandwriting(@NonNull IInputMethodClient client) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.startStylusHandwriting(client);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static boolean isStylusHandwritingAvailableAsUser(@UserIdInt int userId) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return false;
+        }
+        try {
+            return service.isStylusHandwritingAvailableAsUser(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static void addVirtualStylusIdForTestSession(IInputMethodClient client) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.addVirtualStylusIdForTestSession(client);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @AnyThread
+    static void setStylusWindowIdleTimeoutForTest(
+            IInputMethodClient client, @DurationMillisLong long timeout) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return;
+        }
+        try {
+            service.setStylusWindowIdleTimeoutForTest(client, timeout);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
deleted file mode 100644
index 01e8b34..0000000
--- a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
+++ /dev/null
@@ -1,288 +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 android.view.inputmethod;
-
-import android.annotation.AnyThread;
-import android.annotation.DurationMillisLong;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.window.ImeOnBackInvokedDispatcher;
-
-import com.android.internal.inputmethod.DirectBootAwareness;
-import com.android.internal.inputmethod.IInputMethodClient;
-import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
-import com.android.internal.inputmethod.IRemoteInputConnection;
-import com.android.internal.inputmethod.InputBindResult;
-import com.android.internal.inputmethod.SoftInputShowHideReason;
-import com.android.internal.inputmethod.StartInputFlags;
-import com.android.internal.inputmethod.StartInputReason;
-import com.android.internal.view.IInputMethodManager;
-
-import java.util.List;
-
-/**
- * A wrapper class to invoke IPCs defined in {@link IInputMethodManager}.
- */
-final class IInputMethodManagerInvoker {
-    @NonNull
-    private final IInputMethodManager mTarget;
-
-    private IInputMethodManagerInvoker(@NonNull IInputMethodManager target) {
-        mTarget = target;
-    }
-
-    @AnyThread
-    @NonNull
-    static IInputMethodManagerInvoker create(@NonNull IInputMethodManager imm) {
-        return new IInputMethodManagerInvoker(imm);
-    }
-
-    @AnyThread
-    void addClient(@NonNull IInputMethodClient client,
-            @NonNull IRemoteInputConnection fallbackInputConnection, int untrustedDisplayId) {
-        try {
-            mTarget.addClient(client, fallbackInputConnection, untrustedDisplayId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    @NonNull
-    List<InputMethodInfo> getInputMethodList(@UserIdInt int userId,
-            @DirectBootAwareness int directBootAwareness) {
-        try {
-            return mTarget.getInputMethodList(userId, directBootAwareness);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    @NonNull
-    List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
-        try {
-            return mTarget.getEnabledInputMethodList(userId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    @NonNull
-    List<InputMethodSubtype> getEnabledInputMethodSubtypeList(@Nullable String imiId,
-            boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
-        try {
-            return mTarget.getEnabledInputMethodSubtypeList(imiId,
-                    allowsImplicitlyEnabledSubtypes, userId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    @Nullable
-    InputMethodSubtype getLastInputMethodSubtype(@UserIdInt int userId) {
-        try {
-            return mTarget.getLastInputMethodSubtype(userId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
-            int flags, int lastClickToolType, @Nullable ResultReceiver resultReceiver,
-            @SoftInputShowHideReason int reason) {
-        try {
-            return mTarget.showSoftInput(
-                    client, windowToken, flags, lastClickToolType, resultReceiver, reason);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
-            int flags, @Nullable ResultReceiver resultReceiver,
-            @SoftInputShowHideReason int reason) {
-        try {
-            return mTarget.hideSoftInput(client, windowToken, flags, resultReceiver, reason);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    @NonNull
-    InputBindResult startInputOrWindowGainedFocus(@StartInputReason int startInputReason,
-            @NonNull IInputMethodClient client, @Nullable IBinder windowToken,
-            @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
-            @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
-            @Nullable IRemoteInputConnection remoteInputConnection,
-            @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
-        try {
-            return mTarget.startInputOrWindowGainedFocus(startInputReason, client, windowToken,
-                    startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
-                    remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
-                    imeDispatcher);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    void showInputMethodPickerFromClient(@NonNull IInputMethodClient client,
-            int auxiliarySubtypeMode) {
-        try {
-            mTarget.showInputMethodPickerFromClient(client, auxiliarySubtypeMode);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    void showInputMethodPickerFromSystem(@NonNull IInputMethodClient client,
-            int auxiliarySubtypeMode, int displayId) {
-        try {
-            mTarget.showInputMethodPickerFromSystem(client, auxiliarySubtypeMode, displayId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    boolean isInputMethodPickerShownForTest() {
-        try {
-            return mTarget.isInputMethodPickerShownForTest();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    @Nullable
-    InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId) {
-        try {
-            return mTarget.getCurrentInputMethodSubtype(userId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    void setAdditionalInputMethodSubtypes(@NonNull String imeId,
-            @NonNull InputMethodSubtype[] subtypes, @UserIdInt int userId) {
-        try {
-            mTarget.setAdditionalInputMethodSubtypes(imeId, subtypes, userId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    void setExplicitlyEnabledInputMethodSubtypes(@NonNull String imeId,
-            @NonNull int[] subtypeHashCodes, @UserIdInt int userId) {
-        try {
-            mTarget.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    int getInputMethodWindowVisibleHeight(@NonNull IInputMethodClient client) {
-        try {
-            return mTarget.getInputMethodWindowVisibleHeight(client);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    void reportVirtualDisplayGeometryAsync(@NonNull IInputMethodClient client, int childDisplayId,
-            @Nullable float[] matrixValues) {
-        try {
-            mTarget.reportVirtualDisplayGeometryAsync(client, childDisplayId, matrixValues);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    void reportPerceptibleAsync(@NonNull IBinder windowToken, boolean perceptible) {
-        try {
-            mTarget.reportPerceptibleAsync(windowToken, perceptible);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    void removeImeSurfaceFromWindowAsync(@NonNull IBinder windowToken) {
-        try {
-            mTarget.removeImeSurfaceFromWindowAsync(windowToken);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    void startStylusHandwriting(@NonNull IInputMethodClient client) {
-        try {
-            mTarget.startStylusHandwriting(client);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    boolean isStylusHandwritingAvailableAsUser(@UserIdInt int userId) {
-        try {
-            return mTarget.isStylusHandwritingAvailableAsUser(userId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    void addVirtualStylusIdForTestSession(IInputMethodClient client) {
-        try {
-            mTarget.addVirtualStylusIdForTestSession(client);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @AnyThread
-    void setStylusWindowIdleTimeoutForTest(
-            IInputMethodClient client, @DurationMillisLong long timeout) {
-        try {
-            mTarget.setStylusWindowIdleTimeoutForTest(client, timeout);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 7d268a9..d6d7339 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -16,11 +16,14 @@
 
 package android.view.inputmethod;
 
+import static android.view.inputmethod.TextBoundsInfoResult.CODE_UNSUPPORTED;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.RectF;
 import android.inputmethodservice.InputMethodService;
 import android.os.Bundle;
 import android.os.Handler;
@@ -32,7 +35,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 
 /**
@@ -1205,6 +1210,40 @@
         return false;
     }
 
+
+    /**
+     * Called by input method to request the {@link TextBoundsInfo} for a range of text which is
+     * covered by or in vicinity of the given {@code RectF}. It can be used as a supplementary
+     * method to implement the handwriting gesture API -
+     * {@link #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)}.
+     *
+     * <p><strong>Editor authors</strong>: It's preferred that the editor returns a
+     * {@link TextBoundsInfo} of all the text lines whose bounds intersect with the given
+     * {@code rectF}.
+     * </p>
+     *
+     * <p><strong>IME authors</strong>: This method is expensive when the text is long. Please
+     * consider that both the text bounds computation and IPC round-trip to send the data are time
+     * consuming. It's preferable to only request text bounds in smaller areas.
+     * </p>
+     *
+     * @param rectF the interested area where the text bounds are requested, in the screen
+     *              coordinates.
+     * @param executor the executor to run the callback.
+     * @param consumer the callback invoked by editor to return the result. It must return a
+     *                 non-null object.
+     *
+     * @see TextBoundsInfo
+     * @see android.view.inputmethod.TextBoundsInfoResult
+     */
+    default void requestTextBoundsInfo(
+            @NonNull RectF rectF, @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<TextBoundsInfoResult> consumer) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(consumer);
+        executor.execute(() -> consumer.accept(new TextBoundsInfoResult(CODE_UNSUPPORTED)));
+    }
+
     /**
      * Called by the system to enable application developers to specify a dedicated thread on which
      * {@link InputConnection} methods are called back.
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 56beddf..7af96b6 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.RectF;
 import android.os.Bundle;
 import android.os.Handler;
 import android.view.KeyEvent;
@@ -27,6 +28,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 
 /**
@@ -347,6 +349,17 @@
      * @throws NullPointerException if the target is {@code null}.
      */
     @Override
+    public void requestTextBoundsInfo(
+            @NonNull RectF rectF, @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<TextBoundsInfoResult> consumer) {
+        mTarget.requestTextBoundsInfo(rectF, executor, consumer);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws NullPointerException if the target is {@code null}.
+     */
+    @Override
     public Handler getHandler() {
         return mTarget.getHandler();
     }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 69eed0a..1697bf8 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -70,8 +70,6 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.ResultReceiver;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -295,7 +293,7 @@
     private static final String SUBTYPE_MODE_VOICE = "voice";
 
     /**
-     * Provide this to {@link IInputMethodManagerInvoker#startInputOrWindowGainedFocus(int,
+     * Provide this to {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocus(int,
      * IInputMethodClient, IBinder, int, int, int, EditorInfo,
      * com.android.internal.inputmethod.IRemoteInputConnection, IRemoteAccessibilityInputConnection,
      * int, int, ImeOnBackInvokedDispatcher)} to receive
@@ -422,16 +420,13 @@
             SystemProperties.getBoolean("debug.imm.optimize_noneditable_views", true);
 
     /**
-     * @deprecated Use {@link #mServiceInvoker} instead.
+     * @deprecated Use {@link IInputMethodManagerGlobalInvoker} instead.
      */
     @Deprecated
     @UnsupportedAppUsage
     final IInputMethodManager mService;
     private final Looper mMainLooper;
 
-    @NonNull
-    private final IInputMethodManagerInvoker mServiceInvoker;
-
     // For scheduling work on the main thread.  This also serves as our
     // global lock.
     // Remark on @UnsupportedAppUsage: there were context leaks on old versions
@@ -735,7 +730,7 @@
      * @hide
      */
     public void reportPerceptible(@NonNull IBinder windowToken, boolean perceptible) {
-        mServiceInvoker.reportPerceptibleAsync(windowToken, perceptible);
+        IInputMethodManagerGlobalInvoker.reportPerceptibleAsync(windowToken, perceptible);
     }
 
     private final class DelegateImpl implements
@@ -808,7 +803,7 @@
                 }
 
                 // ignore the result
-                mServiceInvoker.startInputOrWindowGainedFocus(
+                IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
                         StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
                         viewForWindowFocus.getWindowToken(), startInputFlags, softInputMode,
                         windowFlags,
@@ -1353,6 +1348,10 @@
         return false;
     }
 
+    static boolean isInEditModeInternal() {
+        return isInEditMode();
+    }
+
     @NonNull
     private static InputMethodManager createInstance(int displayId, Looper looper) {
         return isInEditMode() ? createStubInstance(displayId, looper)
@@ -1361,12 +1360,9 @@
 
     @NonNull
     private static InputMethodManager createRealInstance(int displayId, Looper looper) {
-        final IInputMethodManager service;
-        try {
-            service = IInputMethodManager.Stub.asInterface(
-                    ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
-        } catch (ServiceNotFoundException e) {
-            throw new IllegalStateException(e);
+        final IInputMethodManager service = IInputMethodManagerGlobalInvoker.getService();
+        if (service == null) {
+            throw new IllegalStateException("IInputMethodManager is not available");
         }
         final InputMethodManager imm = new InputMethodManager(service, displayId, looper);
         // InputMethodManagerService#addClient() relies on Binder.getCalling{Pid, Uid}() to
@@ -1378,7 +1374,8 @@
         // 1) doing so has no effect for A and 2) doing so is sufficient for B.
         final long identity = Binder.clearCallingIdentity();
         try {
-            imm.mServiceInvoker.addClient(imm.mClient, imm.mFallbackInputConnection, displayId);
+            IInputMethodManagerGlobalInvoker.addClient(imm.mClient, imm.mFallbackInputConnection,
+                    displayId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1418,7 +1415,6 @@
 
     private InputMethodManager(@NonNull IInputMethodManager service, int displayId, Looper looper) {
         mService = service;  // For @UnsupportedAppUsage
-        mServiceInvoker = IInputMethodManagerInvoker.create(service);
         mMainLooper = looper;
         mH = new H(looper);
         mDisplayId = displayId;
@@ -1512,7 +1508,8 @@
         // We intentionally do not use UserHandle.getCallingUserId() here because for system
         // services InputMethodManagerInternal.getInputMethodListAsUser() should be used
         // instead.
-        return mServiceInvoker.getInputMethodList(UserHandle.myUserId(), DirectBootAwareness.AUTO);
+        return IInputMethodManagerGlobalInvoker.getInputMethodList(UserHandle.myUserId(),
+                DirectBootAwareness.AUTO);
     }
 
     /**
@@ -1546,7 +1543,7 @@
             }
             return false;
         }
-        return mServiceInvoker.isStylusHandwritingAvailableAsUser(userId);
+        return IInputMethodManagerGlobalInvoker.isStylusHandwritingAvailableAsUser(userId);
     }
 
     /**
@@ -1560,7 +1557,8 @@
     @RequiresPermission(INTERACT_ACROSS_USERS_FULL)
     @NonNull
     public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
-        return mServiceInvoker.getInputMethodList(userId, DirectBootAwareness.AUTO);
+        return IInputMethodManagerGlobalInvoker.getInputMethodList(userId,
+                DirectBootAwareness.AUTO);
     }
 
     /**
@@ -1576,7 +1574,7 @@
     @NonNull
     public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId,
             @DirectBootAwareness int directBootAwareness) {
-        return mServiceInvoker.getInputMethodList(userId, directBootAwareness);
+        return IInputMethodManagerGlobalInvoker.getInputMethodList(userId, directBootAwareness);
     }
 
     /**
@@ -1591,7 +1589,7 @@
         // We intentionally do not use UserHandle.getCallingUserId() here because for system
         // services InputMethodManagerInternal.getEnabledInputMethodListAsUser() should be used
         // instead.
-        return mServiceInvoker.getEnabledInputMethodList(UserHandle.myUserId());
+        return IInputMethodManagerGlobalInvoker.getEnabledInputMethodList(UserHandle.myUserId());
     }
 
     /**
@@ -1603,7 +1601,7 @@
      */
     @RequiresPermission(INTERACT_ACROSS_USERS_FULL)
     public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
-        return mServiceInvoker.getEnabledInputMethodList(userId);
+        return IInputMethodManagerGlobalInvoker.getEnabledInputMethodList(userId);
     }
 
     /**
@@ -1620,7 +1618,7 @@
     @NonNull
     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(@Nullable InputMethodInfo imi,
             boolean allowsImplicitlyEnabledSubtypes) {
-        return mServiceInvoker.getEnabledInputMethodSubtypeList(
+        return IInputMethodManagerGlobalInvoker.getEnabledInputMethodSubtypeList(
                 imi == null ? null : imi.getId(),
                 allowsImplicitlyEnabledSubtypes,
                 UserHandle.myUserId());
@@ -1904,8 +1902,11 @@
      * a result receiver: explicitly request that the current input method's
      * soft input area be shown to the user, if needed.
      *
-     * @param view The currently focused view, which would like to receive
-     * soft keyboard input.
+     * @param view The currently focused view, which would like to receive soft keyboard input.
+     *             Note that this view is only considered focused here if both it itself has
+     *             {@link View#isFocused view focus}, and its containing window has
+     *             {@link View#hasWindowFocus window focus}. Otherwise the call fails and
+     *             returns {@code false}.
      * @param flags Provides additional operating flags.  Currently may be
      * 0 or have the {@link #SHOW_IMPLICIT} bit set.
      */
@@ -1967,8 +1968,11 @@
      * can be garbage collected regardless of the lifetime of
      * {@link ResultReceiver}.
      *
-     * @param view The currently focused view, which would like to receive
-     * soft keyboard input.
+     * @param view The currently focused view, which would like to receive soft keyboard input.
+     *             Note that this view is only considered focused here if both it itself has
+     *             {@link View#isFocused view focus}, and its containing window has
+     *             {@link View#hasWindowFocus window focus}. Otherwise the call fails and
+     *             returns {@code false}.
      * @param flags Provides additional operating flags.  Currently may be
      * 0 or have the {@link #SHOW_IMPLICIT} bit set.
      * @param resultReceiver If non-null, this will be called by the IME when
@@ -2003,7 +2007,7 @@
             mH.executeOrSendMessage(Message.obtain(mH, MSG_ON_SHOW_REQUESTED));
             Log.d(TAG, "showSoftInput() view=" + view + " flags=" + flags + " reason="
                     + InputMethodDebug.softInputDisplayReasonToString(reason));
-            return mServiceInvoker.showSoftInput(
+            return IInputMethodManagerGlobalInvoker.showSoftInput(
                     mClient,
                     view.getWindowToken(),
                     flags,
@@ -2035,7 +2039,7 @@
             // Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread.
             // TODO(b/229426865): call WindowInsetsController#show instead.
             mH.executeOrSendMessage(Message.obtain(mH, MSG_ON_SHOW_REQUESTED));
-            mServiceInvoker.showSoftInput(
+            IInputMethodManagerGlobalInvoker.showSoftInput(
                     mClient,
                     mCurRootView.getView().getWindowToken(),
                     flags,
@@ -2116,8 +2120,8 @@
                 return false;
             }
 
-            return mServiceInvoker.hideSoftInput(mClient, windowToken, flags, resultReceiver,
-                    reason);
+            return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, flags,
+                    resultReceiver, reason);
         }
     }
 
@@ -2165,7 +2169,7 @@
                 return;
             }
 
-            mServiceInvoker.startStylusHandwriting(mClient);
+            IInputMethodManagerGlobalInvoker.startStylusHandwriting(mClient);
             // TODO(b/210039666): do we need any extra work for supporting non-native
             //   UI toolkits?
         }
@@ -2498,7 +2502,7 @@
             }
             final int targetUserId = editorInfo.targetInputMethodUser != null
                     ? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId();
-            res = mServiceInvoker.startInputOrWindowGainedFocus(
+            res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
                     startInputReason, mClient, windowGainingFocus, startInputFlags,
                     softInputMode, windowFlags, editorInfo, servedInputConnection,
                     servedInputConnection == null ? null
@@ -2594,7 +2598,7 @@
     @TestApi
     public void addVirtualStylusIdForTestSession() {
         synchronized (mH) {
-            mServiceInvoker.addVirtualStylusIdForTestSession(mClient);
+            IInputMethodManagerGlobalInvoker.addVirtualStylusIdForTestSession(mClient);
         }
     }
 
@@ -2608,7 +2612,7 @@
     @TestApi
     public void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) {
         synchronized (mH) {
-            mServiceInvoker.setStylusWindowIdleTimeoutForTest(mClient, timeout);
+            IInputMethodManagerGlobalInvoker.setStylusWindowIdleTimeoutForTest(mClient, timeout);
         }
     }
 
@@ -2745,7 +2749,7 @@
                 Log.w(TAG, "No current root view, ignoring closeCurrentInput()");
                 return;
             }
-            mServiceInvoker.hideSoftInput(
+            IInputMethodManagerGlobalInvoker.hideSoftInput(
                     mClient,
                     mCurRootView.getView().getWindowToken(),
                     HIDE_NOT_ALWAYS,
@@ -2821,7 +2825,7 @@
         synchronized (mH) {
             if (isImeSessionAvailableLocked() && mCurRootView != null
                     && mCurRootView.getWindowToken() == windowToken) {
-                mServiceInvoker.hideSoftInput(mClient, windowToken, 0 /* flags */,
+                IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, 0 /* flags */,
                         null /* resultReceiver */,
                         SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
             }
@@ -2835,7 +2839,7 @@
      */
     public void removeImeSurface(@NonNull IBinder windowToken) {
         synchronized (mH) {
-            mServiceInvoker.removeImeSurfaceFromWindowAsync(windowToken);
+            IInputMethodManagerGlobalInvoker.removeImeSurfaceFromWindowAsync(windowToken);
         }
     }
 
@@ -3440,12 +3444,13 @@
         final int mode = showAuxiliarySubtypes
                 ? SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES
                 : SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES;
-        mServiceInvoker.showInputMethodPickerFromSystem(mClient, mode, displayId);
+        IInputMethodManagerGlobalInvoker.showInputMethodPickerFromSystem(mClient, mode, displayId);
     }
 
     @GuardedBy("mH")
     private void showInputMethodPickerLocked() {
-        mServiceInvoker.showInputMethodPickerFromClient(mClient, SHOW_IM_PICKER_MODE_AUTO);
+        IInputMethodManagerGlobalInvoker.showInputMethodPickerFromClient(mClient,
+                SHOW_IM_PICKER_MODE_AUTO);
     }
 
     /**
@@ -3461,7 +3466,7 @@
      */
     @TestApi
     public boolean isInputMethodPickerShown() {
-        return mServiceInvoker.isInputMethodPickerShownForTest();
+        return IInputMethodManagerGlobalInvoker.isInputMethodPickerShownForTest();
     }
 
     /**
@@ -3500,7 +3505,7 @@
      */
     @Nullable
     public InputMethodSubtype getCurrentInputMethodSubtype() {
-        return mServiceInvoker.getCurrentInputMethodSubtype(UserHandle.myUserId());
+        return IInputMethodManagerGlobalInvoker.getCurrentInputMethodSubtype(UserHandle.myUserId());
     }
 
     /**
@@ -3545,7 +3550,7 @@
             return false;
         }
         final List<InputMethodSubtype> enabledSubtypes =
-                mServiceInvoker.getEnabledInputMethodSubtypeList(imeId, true,
+                IInputMethodManagerGlobalInvoker.getEnabledInputMethodSubtypeList(imeId, true,
                         UserHandle.myUserId());
         final int numSubtypes = enabledSubtypes.size();
         for (int i = 0; i < numSubtypes; ++i) {
@@ -3611,7 +3616,7 @@
     @UnsupportedAppUsage(trackingBug = 204906124, maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
             publicAlternatives = "Use {@link android.view.WindowInsets} instead")
     public int getInputMethodWindowVisibleHeight() {
-        return mServiceInvoker.getInputMethodWindowVisibleHeight(mClient);
+        return IInputMethodManagerGlobalInvoker.getInputMethodWindowVisibleHeight(mClient);
     }
 
     /**
@@ -3631,7 +3636,8 @@
             matrixValues = new float[9];
             matrix.getValues(matrixValues);
         }
-        mServiceInvoker.reportVirtualDisplayGeometryAsync(mClient, childDisplayId, matrixValues);
+        IInputMethodManagerGlobalInvoker.reportVirtualDisplayGeometryAsync(mClient, childDisplayId,
+                matrixValues);
     }
 
     /**
@@ -3738,7 +3744,8 @@
     @Deprecated
     public void setAdditionalInputMethodSubtypes(@NonNull String imiId,
             @NonNull InputMethodSubtype[] subtypes) {
-        mServiceInvoker.setAdditionalInputMethodSubtypes(imiId, subtypes, UserHandle.myUserId());
+        IInputMethodManagerGlobalInvoker.setAdditionalInputMethodSubtypes(imiId, subtypes,
+                UserHandle.myUserId());
     }
 
     /**
@@ -3787,8 +3794,8 @@
      */
     public void setExplicitlyEnabledInputMethodSubtypes(@NonNull String imiId,
             @NonNull int[] subtypeHashCodes) {
-        mServiceInvoker.setExplicitlyEnabledInputMethodSubtypes(imiId, subtypeHashCodes,
-                UserHandle.myUserId());
+        IInputMethodManagerGlobalInvoker.setExplicitlyEnabledInputMethodSubtypes(imiId,
+                subtypeHashCodes, UserHandle.myUserId());
     }
 
     /**
@@ -3798,7 +3805,7 @@
      */
     @Nullable
     public InputMethodSubtype getLastInputMethodSubtype() {
-        return mServiceInvoker.getLastInputMethodSubtype(UserHandle.myUserId());
+        return IInputMethodManagerGlobalInvoker.getLastInputMethodSubtype(UserHandle.myUserId());
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputMethodManagerGlobal.java b/core/java/android/view/inputmethod/InputMethodManagerGlobal.java
new file mode 100644
index 0000000..63d9167
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodManagerGlobal.java
@@ -0,0 +1,104 @@
+/*
+ * 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 android.view.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.Nullable;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.os.RemoteException;
+
+import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.view.IInputMethodManager;
+
+import java.util.function.Consumer;
+
+/**
+ * Defines a set of static methods that can be used globally by framework classes.
+ *
+ * @hide
+ */
+public class InputMethodManagerGlobal {
+    /**
+     * @return {@code true} if IME tracing is currently is available.
+     */
+    @AnyThread
+    public static boolean isImeTraceAvailable() {
+        return IInputMethodManagerGlobalInvoker.isAvailable();
+    }
+
+    /**
+     * Invokes {@link IInputMethodManager#startProtoDump(byte[], int, String)}.
+     *
+     * @param protoDump client or service side information to be stored by the server
+     * @param source where the information is coming from, refer to
+     *               {@link ImeTracing#IME_TRACING_FROM_CLIENT} and
+     *               {@link ImeTracing#IME_TRACING_FROM_IMS}
+     * @param where where the information is coming from.
+     * @param exceptionHandler an optional {@link RemoteException} handler.
+     */
+    @RequiresNoPermission
+    @AnyThread
+    public static void startProtoDump(byte[] protoDump, int source, String where,
+            @Nullable Consumer<RemoteException> exceptionHandler) {
+        IInputMethodManagerGlobalInvoker.startProtoDump(protoDump, source, where, exceptionHandler);
+    }
+
+    /**
+     * Invokes {@link IInputMethodManager#startImeTrace()}.
+     *
+     * @param exceptionHandler an optional {@link RemoteException} handler.
+     */
+    @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
+    @AnyThread
+    public static void startImeTrace(@Nullable Consumer<RemoteException> exceptionHandler) {
+        IInputMethodManagerGlobalInvoker.startImeTrace(exceptionHandler);
+    }
+
+    /**
+     * Invokes {@link IInputMethodManager#stopImeTrace()}.
+     *
+     * @param exceptionHandler an optional {@link RemoteException} handler.
+     */
+    @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
+    @AnyThread
+    public static void stopImeTrace(@Nullable Consumer<RemoteException> exceptionHandler) {
+        IInputMethodManagerGlobalInvoker.stopImeTrace(exceptionHandler);
+    }
+
+    /**
+     * Invokes {@link IInputMethodManager#isImeTraceEnabled()}.
+     *
+     * @return The return value of {@link IInputMethodManager#isImeTraceEnabled()}.
+     */
+    @RequiresNoPermission
+    @AnyThread
+    public static boolean isImeTraceEnabled() {
+        return IInputMethodManagerGlobalInvoker.isImeTraceEnabled();
+    }
+
+    /**
+     * Invokes {@link IInputMethodManager#removeImeSurface()}
+     *
+     * @param exceptionHandler an optional {@link RemoteException} handler.
+     */
+    @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @AnyThread
+    public static void removeImeSurface(@Nullable Consumer<RemoteException> exceptionHandler) {
+        IInputMethodManagerGlobalInvoker.removeImeSurface(exceptionHandler);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt b/core/java/android/view/inputmethod/ParcelableHandwritingGesture.aidl
similarity index 72%
copy from packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
copy to core/java/android/view/inputmethod/ParcelableHandwritingGesture.aidl
index 581dafa3..ffadf82 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
+++ b/core/java/android/view/inputmethod/ParcelableHandwritingGesture.aidl
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.model
+package android.view.inputmethod;
 
-/** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
-enum class KeyguardQuickAffordancePosition {
-    BOTTOM_START,
-    BOTTOM_END,
-}
+parcelable ParcelableHandwritingGesture;
diff --git a/core/java/android/view/inputmethod/ParcelableHandwritingGesture.java b/core/java/android/view/inputmethod/ParcelableHandwritingGesture.java
new file mode 100644
index 0000000..e4066fc
--- /dev/null
+++ b/core/java/android/view/inputmethod/ParcelableHandwritingGesture.java
@@ -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 android.view.inputmethod;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A generic container of parcelable {@link HandwritingGesture}.
+ *
+ * @hide
+ */
+public final class ParcelableHandwritingGesture implements Parcelable {
+    @NonNull
+    private final HandwritingGesture mGesture;
+    @NonNull
+    private final Parcelable mGestureAsParcelable;
+
+    private ParcelableHandwritingGesture(@NonNull HandwritingGesture gesture) {
+        mGesture = gesture;
+        // For fail-fast.
+        mGestureAsParcelable = (Parcelable) gesture;
+    }
+
+    /**
+     * Creates {@link ParcelableHandwritingGesture} from {@link HandwritingGesture}, which also
+     * implements {@link Parcelable}.
+     *
+     * @param gesture {@link HandwritingGesture} object to be stored.
+     * @return {@link ParcelableHandwritingGesture} to be stored in {@link Parcel}.
+     */
+    @NonNull
+    public static ParcelableHandwritingGesture of(@NonNull HandwritingGesture gesture) {
+        return new ParcelableHandwritingGesture(Objects.requireNonNull(gesture));
+    }
+
+    /**
+     * @return {@link HandwritingGesture} object stored in this container.
+     */
+    @NonNull
+    public HandwritingGesture get() {
+        return mGesture;
+    }
+
+    private static HandwritingGesture createFromParcelInternal(
+            @HandwritingGesture.GestureType int gestureType, @NonNull Parcel parcel) {
+        switch (gestureType) {
+            case HandwritingGesture.GESTURE_TYPE_NONE:
+                throw new UnsupportedOperationException("GESTURE_TYPE_NONE is not supported");
+            case HandwritingGesture.GESTURE_TYPE_SELECT:
+                return SelectGesture.CREATOR.createFromParcel(parcel);
+            case HandwritingGesture.GESTURE_TYPE_SELECT_RANGE:
+                return SelectRangeGesture.CREATOR.createFromParcel(parcel);
+            case HandwritingGesture.GESTURE_TYPE_INSERT:
+                return InsertGesture.CREATOR.createFromParcel(parcel);
+            case HandwritingGesture.GESTURE_TYPE_DELETE:
+                return DeleteGesture.CREATOR.createFromParcel(parcel);
+            case HandwritingGesture.GESTURE_TYPE_DELETE_RANGE:
+                return DeleteRangeGesture.CREATOR.createFromParcel(parcel);
+            case HandwritingGesture.GESTURE_TYPE_JOIN_OR_SPLIT:
+                return JoinOrSplitGesture.CREATOR.createFromParcel(parcel);
+            case HandwritingGesture.GESTURE_TYPE_REMOVE_SPACE:
+                return RemoveSpaceGesture.CREATOR.createFromParcel(parcel);
+            default:
+                throw new UnsupportedOperationException("Unknown type=" + gestureType);
+        }
+    }
+
+    public static final Creator<ParcelableHandwritingGesture> CREATOR = new Parcelable.Creator<>() {
+        @Override
+        public ParcelableHandwritingGesture createFromParcel(Parcel in) {
+            final int gestureType = in.readInt();
+            return new ParcelableHandwritingGesture(createFromParcelInternal(gestureType, in));
+        }
+
+        @Override
+        public ParcelableHandwritingGesture[] newArray(int size) {
+            return new ParcelableHandwritingGesture[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return mGestureAsParcelable.describeContents();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mGesture.getGestureType());
+        mGestureAsParcelable.writeToParcel(dest, flags);
+    }
+}
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index f2b7099..ead7924 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -28,6 +28,7 @@
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.RectF;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -982,62 +983,9 @@
 
     @Dispatching(cancellable = true)
     @Override
-    public void performHandwritingSelectGesture(
-            InputConnectionCommandHeader header, SelectGesture gesture,
+    public void performHandwritingGesture(
+            InputConnectionCommandHeader header, ParcelableHandwritingGesture gestureContainer,
             ResultReceiver resultReceiver) {
-        performHandwritingGestureInternal(header, gesture, resultReceiver);
-    }
-
-    @Dispatching(cancellable = true)
-    @Override
-    public void performHandwritingSelectRangeGesture(
-            InputConnectionCommandHeader header, SelectRangeGesture gesture,
-            ResultReceiver resultReceiver) {
-        performHandwritingGestureInternal(header, gesture, resultReceiver);
-    }
-
-    @Dispatching(cancellable = true)
-    @Override
-    public void performHandwritingInsertGesture(
-            InputConnectionCommandHeader header, InsertGesture gesture,
-            ResultReceiver resultReceiver) {
-        performHandwritingGestureInternal(header, gesture, resultReceiver);
-    }
-
-    @Dispatching(cancellable = true)
-    @Override
-    public void performHandwritingDeleteGesture(
-            InputConnectionCommandHeader header, DeleteGesture gesture,
-            ResultReceiver resultReceiver) {
-        performHandwritingGestureInternal(header, gesture, resultReceiver);
-    }
-
-    @Dispatching(cancellable = true)
-    @Override
-    public void performHandwritingDeleteRangeGesture(
-            InputConnectionCommandHeader header, DeleteRangeGesture gesture,
-            ResultReceiver resultReceiver) {
-        performHandwritingGestureInternal(header, gesture, resultReceiver);
-    }
-
-    @Dispatching(cancellable = true)
-    @Override
-    public void performHandwritingRemoveSpaceGesture(
-            InputConnectionCommandHeader header, RemoveSpaceGesture gesture,
-            ResultReceiver resultReceiver) {
-        performHandwritingGestureInternal(header, gesture, resultReceiver);
-    }
-
-    @Dispatching(cancellable = true)
-    @Override
-    public void performHandwritingJoinOrSplitGesture(
-            InputConnectionCommandHeader header, JoinOrSplitGesture gesture,
-            ResultReceiver resultReceiver) {
-        performHandwritingGestureInternal(header, gesture, resultReceiver);
-    }
-
-    private <T extends HandwritingGesture> void performHandwritingGestureInternal(
-            InputConnectionCommandHeader header,  T gesture, ResultReceiver resultReceiver) {
         dispatchWithTracing("performHandwritingGesture", () -> {
             if (header.mSessionId != mCurrentSessionId.get()) {
                 if (resultReceiver != null) {
@@ -1059,7 +1007,7 @@
             // TODO(210039666): implement Cleaner to return HANDWRITING_GESTURE_RESULT_UNKNOWN if
             //  editor doesn't return any type.
             ic.performHandwritingGesture(
-                    gesture,
+                    gestureContainer.get(),
                     resultReceiver != null ? Runnable::run : null,
                     resultReceiver != null
                             ? (resultCode) -> resultReceiver.send(resultCode, null /* resultData */)
@@ -1147,6 +1095,36 @@
 
     @Dispatching(cancellable = true)
     @Override
+    public void requestTextBoundsInfo(
+            InputConnectionCommandHeader header, RectF rectF,
+            @NonNull ResultReceiver resultReceiver) {
+        dispatchWithTracing("requestTextBoundsInfo", () -> {
+            if (header.mSessionId != mCurrentSessionId.get()) {
+                resultReceiver.send(TextBoundsInfoResult.CODE_CANCELLED, null);
+                return;  // cancelled
+            }
+            InputConnection ic = getInputConnection();
+            if (ic == null || !isActive()) {
+                Log.w(TAG, "requestTextBoundsInfo on inactive InputConnection");
+                resultReceiver.send(TextBoundsInfoResult.CODE_CANCELLED, null);
+                return;
+            }
+
+            ic.requestTextBoundsInfo(
+                    rectF,
+                    Runnable::run,
+                    (textBoundsInfoResult) -> {
+                        final int resultCode = textBoundsInfoResult.getResultCode();
+                        final TextBoundsInfo textBoundsInfo =
+                                textBoundsInfoResult.getTextBoundsInfo();
+                        resultReceiver.send(resultCode,
+                                textBoundsInfo == null ? null : textBoundsInfo.toBundle());
+                    });
+        });
+    }
+
+    @Dispatching(cancellable = true)
+    @Override
     public void commitContent(InputConnectionCommandHeader header,
             InputContentInfo inputContentInfo, int flags, Bundle opts,
             AndroidFuture future /* T=Boolean */) {
diff --git a/core/java/android/view/inputmethod/TextBoundsInfo.java b/core/java/android/view/inputmethod/TextBoundsInfo.java
new file mode 100644
index 0000000..4e87405
--- /dev/null
+++ b/core/java/android/view/inputmethod/TextBoundsInfo.java
@@ -0,0 +1,844 @@
+/*
+ * 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 android.view.inputmethod;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.SegmentFinder;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.GrowingArrayUtils;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * The text bounds information of a slice of text in the editor.
+ *
+ * <p> This class provides IME the layout information of the text within the range from
+ * {@link #getStart()} to {@link #getEnd()}. It's intended to be used by IME as a supplementary API
+ * to support handwriting gestures.
+ * </p>
+ */
+public final class TextBoundsInfo implements Parcelable {
+    /**
+     * The flag indicating that the character is a whitespace.
+     *
+     * @see Builder#setCharacterFlags(int[])
+     * @see #getCharacterFlags(int)
+     */
+    public static final int FLAG_CHARACTER_WHITESPACE = 1;
+
+    /**
+     * The flag indicating that the character is a linefeed character.
+     *
+     * @see Builder#setCharacterFlags(int[])
+     * @see #getCharacterFlags(int)
+     */
+    public static final int FLAG_CHARACTER_LINEFEED = 1 << 1;
+
+    /**
+     * The flag indicating that the character is a punctuation.
+     *
+     * @see Builder#setCharacterFlags(int[])
+     * @see #getCharacterFlags(int)
+     */
+    public static final int FLAG_CHARACTER_PUNCTUATION = 1 << 2;
+
+    /**
+     * The flag indicating that the line this character belongs to has RTL line direction. It's
+     * required that all characters in the same line must have the same direction.
+     *
+     * @see Builder#setCharacterFlags(int[])
+     * @see #getCharacterFlags(int)
+     */
+    public static final int FLAG_LINE_IS_RTL = 1 << 3;
+
+
+    /** @hide */
+    @IntDef(prefix = "FLAG_", flag = true, value = {
+            FLAG_CHARACTER_WHITESPACE,
+            FLAG_CHARACTER_LINEFEED,
+            FLAG_CHARACTER_PUNCTUATION,
+            FLAG_LINE_IS_RTL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CharacterFlags {}
+
+    /** All the valid flags. */
+    private static final int KNOWN_CHARACTER_FLAGS = FLAG_CHARACTER_WHITESPACE
+            | FLAG_CHARACTER_LINEFEED | FLAG_CHARACTER_PUNCTUATION  | FLAG_LINE_IS_RTL;
+
+    /**
+     * The amount of shift to get the character's BiDi level from the internal character flags.
+     */
+    private static final int BIDI_LEVEL_SHIFT = 19;
+
+    /**
+     * The mask used to get the character's BiDi level from the internal character flags.
+     */
+    private static final int BIDI_LEVEL_MASK = 0x7F << BIDI_LEVEL_SHIFT;
+
+    /**
+     * The flag indicating that the character at the index is the start of a line segment.
+     * This flag is only used internally to serialize the {@link SegmentFinder}.
+     *
+     * @see #writeToParcel(Parcel, int)
+     */
+    private static final int FLAG_LINE_SEGMENT_START = 1 << 31;
+
+    /**
+     * The flag indicating that the character at the index is the end of a line segment.
+     * This flag is only used internally to serialize the {@link SegmentFinder}.
+     *
+     * @see #writeToParcel(Parcel, int)
+     */
+    private static final int FLAG_LINE_SEGMENT_END = 1 << 30;
+
+    /**
+     * The flag indicating that the character at the index is the start of a word segment.
+     * This flag is only used internally to serialize the {@link SegmentFinder}.
+     *
+     * @see #writeToParcel(Parcel, int)
+     */
+    private static final int FLAG_WORD_SEGMENT_START = 1 << 29;
+
+    /**
+     * The flag indicating that the character at the index is the end of a word segment.
+     * This flag is only used internally to serialize the {@link SegmentFinder}.
+     *
+     * @see #writeToParcel(Parcel, int)
+     */
+    private static final int FLAG_WORD_SEGMENT_END = 1 << 28;
+
+    /**
+     * The flag indicating that the character at the index is the start of a grapheme segment.
+     * It's only used internally to serialize the {@link SegmentFinder}.
+     *
+     * @see #writeToParcel(Parcel, int)
+     */
+    private static final int FLAG_GRAPHEME_SEGMENT_START = 1 << 27;
+
+    /**
+     * The flag indicating that the character at the index is the end of a grapheme segment.
+     * It's only used internally to serialize the {@link SegmentFinder}.
+     *
+     * @see #writeToParcel(Parcel, int)
+     */
+    private static final int FLAG_GRAPHEME_SEGMENT_END = 1 << 26;
+
+    private final int mStart;
+    private final int mEnd;
+    private final float[] mMatrixValues;
+    private final float[] mCharacterBounds;
+    /**
+     * The array that encodes character and BiDi levels. They are stored together to save memory
+     * space, and it's easier during serialization.
+     */
+    private final int[] mInternalCharacterFlags;
+    private final SegmentFinder mLineSegmentFinder;
+    private final SegmentFinder mWordSegmentFinder;
+    private final SegmentFinder mGraphemeSegmentFinder;
+
+    /**
+     * Returns a new instance of {@link android.graphics.Matrix} that indicates the transformation
+     * matrix that is to be applied other positional data in this class.
+     *
+     * @return a new instance (copy) of the transformation matrix.
+     */
+    @NonNull
+    public Matrix getMatrix() {
+        final Matrix matrix = new Matrix();
+        matrix.setValues(mMatrixValues);
+        return matrix;
+    }
+
+    /**
+     * Returns the index of the first character whose bounds information is available in this
+     * {@link TextBoundsInfo}, inclusive.
+     *
+     * @see Builder#setStartAndEnd(int, int)
+     */
+    public int getStart() {
+        return mStart;
+    }
+
+    /**
+     * Returns the index of the last character whose bounds information is available in this
+     * {@link TextBoundsInfo}, exclusive.
+     *
+     * @see Builder#setStartAndEnd(int, int)
+     */
+    public int getEnd() {
+        return mEnd;
+    }
+
+    /**
+     * Return the bounds of the character at the given {@code index}, in the coordinates of the
+     * editor.
+     *
+     * @param index the index of the queried character.
+     * @return the bounding box of the queried character.
+     *
+     * @throws IndexOutOfBoundsException if the given {@code index} is out of the range from
+     * the {@code start} to the {@code end}.
+     */
+    @NonNull
+    public RectF getCharacterBounds(int index) {
+        if (index < mStart || index >= mEnd) {
+            throw new IndexOutOfBoundsException("Index is out of the bounds of "
+                    + "[" + mStart + ", " + mEnd + ").");
+        }
+        final int offset = 4 * (index - mStart);
+        return new RectF(mCharacterBounds[offset], mCharacterBounds[offset + 1],
+                mCharacterBounds[offset + 2], mCharacterBounds[offset + 3]);
+    }
+
+    /**
+     * Return the flags associated with the character at the given {@code index}.
+     * The flags contain the following information:
+     * <ul>
+     *     <li>The {@link #FLAG_CHARACTER_WHITESPACE} flag, indicating the character is a
+     *     whitespace. </li>
+     *     <li>The {@link #FLAG_CHARACTER_LINEFEED} flag, indicating the character is a
+     *     linefeed. </li>
+     *     <li>The {@link #FLAG_CHARACTER_PUNCTUATION} flag, indicating the character is a
+     *     punctuation. </li>
+     *     <li>The {@link #FLAG_LINE_IS_RTL} flag, indicating the line this character belongs to
+     *     has RTL line direction. All characters in the same line must have the same line
+     *     direction. Check {@link #getLineSegmentFinder()} for more information of
+     *     line boundaries. </li>
+     * </ul>
+     *
+     * @param index the index of the queried character.
+     * @return the flags associated with the queried character.
+     *
+     * @throws IndexOutOfBoundsException if the given {@code index} is out of the range from
+     * the {@code start} to the {@code end}.
+     *
+     * @see #FLAG_CHARACTER_WHITESPACE
+     * @see #FLAG_CHARACTER_LINEFEED
+     * @see #FLAG_CHARACTER_PUNCTUATION
+     * @see #FLAG_LINE_IS_RTL
+     */
+    @CharacterFlags
+    public int getCharacterFlags(int index) {
+        if (index < mStart || index >= mEnd) {
+            throw new IndexOutOfBoundsException("Index is out of the bounds of "
+                    + "[" + mStart + ", " + mEnd + ").");
+        }
+        final int offset = index - mStart;
+        return mInternalCharacterFlags[offset] & KNOWN_CHARACTER_FLAGS;
+    }
+
+    /**
+     * The BiDi level of the character at the given {@code index}. <br/>
+     * BiDi level is defined by
+     * <a href="https://unicode.org/reports/tr9/#Basic_Display_Algorithm" >the unicode
+     * bidirectional algorithm </a>. One can determine whether a character's direction is
+     * right-to-left (RTL) or left-to-right (LTR) by checking the last bit of the BiDi level.
+     * If it's 1, the character is RTL, otherwise the character is LTR. The BiDi level of a
+     * character must be in the range of [0, 125].
+     *
+     * @param index the index of the queried character.
+     * @return the BiDi level of the character, which is an integer in the range of [0, 125].
+     * @throws IndexOutOfBoundsException if the given {@code index} is out of the range from
+     * the {@code start} to the {@code end}.
+     *
+     * @see Builder#setCharacterBidiLevel(int[])
+     */
+    @IntRange(from = 0, to = 125)
+    public int getCharacterBidiLevel(int index) {
+        if (index < mStart || index >= mEnd) {
+            throw new IndexOutOfBoundsException("Index is out of the bounds of "
+                    + "[" + mStart + ", " + mEnd + ").");
+        }
+        final int offset = index - mStart;
+        return (mInternalCharacterFlags[offset] & BIDI_LEVEL_MASK) >> BIDI_LEVEL_SHIFT;
+    }
+
+    /**
+     * Returns the {@link SegmentFinder} that locates the word boundaries.
+     *
+     * @see Builder#setWordSegmentFinder(SegmentFinder)
+     */
+    @NonNull
+    public SegmentFinder getWordSegmentFinder() {
+        return mWordSegmentFinder;
+    }
+
+    /**
+     * Returns the {@link SegmentFinder} that locates the grapheme boundaries.
+     *
+     * @see Builder#setGraphemeSegmentFinder(SegmentFinder)
+     */
+    @NonNull
+    public SegmentFinder getGraphemeSegmentFinder() {
+        return mGraphemeSegmentFinder;
+    }
+
+    /**
+     * Returns the {@link SegmentFinder} that locates the line boundaries.
+     *
+     * @see Builder#setLineSegmentFinder(SegmentFinder)
+     */
+    @NonNull
+    public SegmentFinder getLineSegmentFinder() {
+        return mLineSegmentFinder;
+    }
+
+    /**
+     * Describe the kinds of special objects contained in this Parcelable
+     * instance's marshaled representation. For example, if the object will
+     * include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
+     * the return value of this method must include the
+     * {@link #CONTENTS_FILE_DESCRIPTOR} bit.
+     *
+     * @return a bitmask indicating the set of special object types marshaled
+     * by this Parcelable object instance.
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Flatten this object in to a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     *              May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mStart);
+        dest.writeInt(mEnd);
+        dest.writeFloatArray(mMatrixValues);
+        dest.writeFloatArray(mCharacterBounds);
+
+        // The end can also be a break position. We need an extra space to encode the breaks.
+        final int[] encodedFlags = Arrays.copyOf(mInternalCharacterFlags, mEnd - mStart + 1);
+        encodeSegmentFinder(encodedFlags, FLAG_GRAPHEME_SEGMENT_START, FLAG_GRAPHEME_SEGMENT_END,
+                mStart, mEnd, mGraphemeSegmentFinder);
+        encodeSegmentFinder(encodedFlags, FLAG_WORD_SEGMENT_START, FLAG_WORD_SEGMENT_END, mStart,
+                mEnd, mWordSegmentFinder);
+        encodeSegmentFinder(encodedFlags, FLAG_LINE_SEGMENT_START, FLAG_LINE_SEGMENT_END, mStart,
+                mEnd, mLineSegmentFinder);
+        dest.writeIntArray(encodedFlags);
+    }
+
+    private TextBoundsInfo(Parcel source) {
+        mStart = source.readInt();
+        mEnd  = source.readInt();
+        mMatrixValues = Objects.requireNonNull(source.createFloatArray());
+        mCharacterBounds = Objects.requireNonNull(source.createFloatArray());
+        final int[] encodedFlags = Objects.requireNonNull(source.createIntArray());
+
+        mGraphemeSegmentFinder = decodeSegmentFinder(encodedFlags, FLAG_GRAPHEME_SEGMENT_START,
+                FLAG_GRAPHEME_SEGMENT_END, mStart, mEnd);
+        mWordSegmentFinder = decodeSegmentFinder(encodedFlags, FLAG_WORD_SEGMENT_START,
+                FLAG_WORD_SEGMENT_END, mStart, mEnd);
+        mLineSegmentFinder = decodeSegmentFinder(encodedFlags, FLAG_LINE_SEGMENT_START,
+                FLAG_LINE_SEGMENT_END, mStart, mEnd);
+
+        final int length = mEnd - mStart;
+        final int flagsMask = KNOWN_CHARACTER_FLAGS | BIDI_LEVEL_MASK;
+        mInternalCharacterFlags = new int[length];
+        for (int i = 0; i < length; ++i) {
+            // Remove the flags used to encoded segment boundaries.
+            mInternalCharacterFlags[i] = encodedFlags[i] & flagsMask;
+        }
+    }
+
+    private TextBoundsInfo(Builder builder) {
+        mStart = builder.mStart;
+        mEnd = builder.mEnd;
+        mMatrixValues = Arrays.copyOf(builder.mMatrixValues, 9);
+        final int length = mEnd - mStart;
+        mCharacterBounds = Arrays.copyOf(builder.mCharacterBounds, 4 * length);
+        // Store characterFlags and characterBidiLevels to save memory.
+        mInternalCharacterFlags = new int[length];
+        for (int index = 0; index < length; ++index) {
+            mInternalCharacterFlags[index] = builder.mCharacterFlags[index]
+                    | (builder.mCharacterBidiLevels[index] << BIDI_LEVEL_SHIFT);
+        }
+        mGraphemeSegmentFinder = builder.mGraphemeSegmentFinder;
+        mWordSegmentFinder = builder.mWordSegmentFinder;
+        mLineSegmentFinder = builder.mLineSegmentFinder;
+    }
+
+    /**
+     * The CREATOR to make this class Parcelable.
+     */
+    @NonNull
+    public static final Parcelable.Creator<TextBoundsInfo> CREATOR = new Creator<TextBoundsInfo>() {
+        @Override
+        public TextBoundsInfo createFromParcel(Parcel source) {
+            return new TextBoundsInfo(source);
+        }
+
+        @Override
+        public TextBoundsInfo[] newArray(int size) {
+            return new TextBoundsInfo[size];
+        }
+    };
+
+    private static final String TEXT_BOUNDS_INFO_KEY = "android.view.inputmethod.TextBoundsInfo";
+
+    /**
+     * Store the {@link TextBoundsInfo} into a {@link Bundle}. This method is used by
+     * {@link RemoteInputConnectionImpl} to transfer the {@link TextBoundsInfo} from the editor
+     * to IME.
+     *
+     * @see TextBoundsInfoResult
+     * @see InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)
+     * @hide
+     */
+    @NonNull
+    public Bundle toBundle() {
+        final Bundle bundle = new Bundle();
+        bundle.putParcelable(TEXT_BOUNDS_INFO_KEY, this);
+        return bundle;
+
+    }
+
+    /** @hide */
+    @Nullable
+    public static TextBoundsInfo createFromBundle(@Nullable Bundle bundle) {
+        if (bundle == null) return null;
+        return bundle.getParcelable(TEXT_BOUNDS_INFO_KEY, TextBoundsInfo.class);
+    }
+
+    /**
+     * The builder class to create a {@link TextBoundsInfo} object.
+     */
+    public static final class Builder {
+        private final float[] mMatrixValues = new float[9];
+        private boolean mMatrixInitialized;
+        private int mStart;
+        private int mEnd;
+        private float[] mCharacterBounds;
+        private int[] mCharacterFlags;
+        private int[] mCharacterBidiLevels;
+        private SegmentFinder mLineSegmentFinder;
+        private SegmentFinder mWordSegmentFinder;
+        private SegmentFinder mGraphemeSegmentFinder;
+
+        /** Clear all the parameters set on this {@link Builder} to reuse it. */
+        @NonNull
+        public Builder clear() {
+            mMatrixInitialized = false;
+            mStart = -1;
+            mEnd = -1;
+            mCharacterBounds = null;
+            mCharacterFlags = null;
+            mLineSegmentFinder = null;
+            mWordSegmentFinder = null;
+            mGraphemeSegmentFinder = null;
+            return this;
+        }
+
+        /**
+         * Sets the matrix that transforms local coordinates into screen coordinates.
+         *
+         * @param matrix transformation matrix from local coordinates into screen coordinates.
+         * @throws NullPointerException if the given {@code matrix} is {@code null}.
+         */
+        @NonNull
+        public Builder setMatrix(@NonNull Matrix matrix) {
+            Objects.requireNonNull(matrix).getValues(mMatrixValues);
+            mMatrixInitialized = true;
+            return this;
+        }
+
+        /**
+         * Set the start and end index of the {@link TextBoundsInfo}. It's the range of the
+         * characters whose information is available in the {@link TextBoundsInfo}.
+         *
+         * @param start the start index of the {@link TextBoundsInfo}, inclusive.
+         * @param end the end index of the {@link TextBoundsInfo}, exclusive.
+         * @throws IllegalArgumentException if the given {@code start} or {@code end} is negative,
+         * or {@code end} is smaller than the {@code start}.
+         */
+        @NonNull
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        public Builder setStartAndEnd(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+            Preconditions.checkArgument(start >= 0);
+            Preconditions.checkArgumentInRange(start, 0, end, "start");
+            mStart = start;
+            mEnd = end;
+            return this;
+        }
+
+        /**
+         * Set the characters bounds, in the coordinates of the editor. <br/>
+         *
+         * The given array should be divided into groups of four where each element represents
+         * left, top, right and bottom of the character bounds respectively.
+         * The bounds of the i-th character in the editor should be stored at index
+         * 4 * (i - start). The length of the given array must equal to 4 * (end - start). <br/>
+         *
+         * Sometimes multiple characters in a single grapheme are rendered as one symbol on the
+         * screen. So those characters only have one shared bounds. In this case, we recommend the
+         * editor to assign all the width to the bounds of the first character in the grapheme,
+         * and make the rest characters' bounds zero-width. <br/>
+         *
+         * For example, the string "'0xD83D' '0xDE00'" is rendered as one grapheme - a grinning face
+         * emoji. If the bounds of the grapheme is: Rect(5, 10, 15, 20), the character bounds of the
+         * string should be: [ Rect(5, 10, 15, 20), Rect(15, 10, 15, 20) ].
+         *
+         * @param characterBounds the array of the flattened character bounds.
+         * @throws NullPointerException if the given {@code characterBounds} is {@code null}.
+         */
+        @NonNull
+        public Builder setCharacterBounds(@NonNull float[] characterBounds) {
+            mCharacterBounds = Objects.requireNonNull(characterBounds);
+            return this;
+        }
+
+        /**
+         * Set the flags of the characters. The flags of the i-th character in the editor is stored
+         * at index (i - start). The length of the given array must equal to (end - start).
+         * The flags contain the following information:
+         * <ul>
+         *     <li>The {@link #FLAG_CHARACTER_WHITESPACE} flag, indicating the character is a
+         *     whitespace. </li>
+         *     <li>The {@link #FLAG_CHARACTER_LINEFEED} flag, indicating the character is a
+         *     linefeed. </li>
+         *     <li>The {@link #FLAG_CHARACTER_PUNCTUATION} flag, indicating the character is a
+         *     punctuation. </li>
+         *     <li>The {@link #FLAG_LINE_IS_RTL} flag, indicating the line this character belongs to
+         *     is RTL. All all character in the same line must have the same line direction. Check
+         *     {@link #getLineSegmentFinder()} for more information of line boundaries. </li>
+         * </ul>
+         *
+         * @param characterFlags the array of the character's flags.
+         * @throws NullPointerException if the given {@code characterFlags} is {@code null}.
+         * @throws IllegalArgumentException if the given {@code characterFlags} contains invalid
+         * flags.
+         *
+         * @see #getCharacterFlags(int)
+         */
+        @NonNull
+        public Builder setCharacterFlags(@NonNull int[] characterFlags) {
+            Objects.requireNonNull(characterFlags);
+            for (int characterFlag : characterFlags) {
+                if ((characterFlag & (~KNOWN_CHARACTER_FLAGS)) != 0) {
+                    throw new IllegalArgumentException("characterFlags contains invalid flags.");
+                }
+            }
+            mCharacterFlags = characterFlags;
+            return this;
+        }
+
+        /**
+         * Set the BiDi levels for the character. The bidiLevel of the i-th character in the editor
+         * is stored at index (i - start). The length of the given array must equal to
+         * (end - start). <br/>
+         *
+         * BiDi level is defined by
+         * <a href="https://unicode.org/reports/tr9/#Basic_Display_Algorithm" >the unicode
+         * bidirectional algorithm </a>. One can determine whether a character's direction is
+         * right-to-left (RTL) or left-to-right (LTR) by checking the last bit of the BiDi level.
+         * If it's 1, the character is RTL, otherwise the character is LTR. The BiDi level of a
+         * character must be in the range of [0, 125].
+         * @param characterBidiLevels the array of the character's BiDi level.
+         *
+         * @throws NullPointerException if the given {@code characterBidiLevels} is {@code null}.
+         * @throws IllegalArgumentException if the given {@code characterBidiLevels} contains an
+         * element that's out of the range [0, 125].
+         *
+         * @see #getCharacterBidiLevel(int)
+         */
+        @NonNull
+        public Builder setCharacterBidiLevel(@NonNull int[] characterBidiLevels) {
+            Objects.requireNonNull(characterBidiLevels);
+            for (int index = 0; index < characterBidiLevels.length; ++index) {
+                Preconditions.checkArgumentInRange(characterBidiLevels[index], 0, 125,
+                        "bidiLevels[" + index + "]");
+            }
+            mCharacterBidiLevels = characterBidiLevels;
+            return this;
+        }
+
+        /**
+         * Set the {@link SegmentFinder} that locates the grapheme cluster boundaries. Grapheme is
+         * defined in <a href="https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries">
+         * the unicode annex #29: unicode text segmentation<a/>. It's a user-perspective character.
+         * And it's usually the minimal unit for selection, backspace, deletion etc. <br/>
+         *
+         * Please note that only the grapheme segments within the range from start to end will
+         * be available to the IME. The remaining information will be discarded during serialization
+         * for better performance.
+         *
+         * @param graphemeSegmentFinder the {@link SegmentFinder} that locates the grapheme cluster
+         *                              boundaries.
+         * @throws NullPointerException if the given {@code graphemeSegmentFinder} is {@code null}.
+         *
+         * @see #getGraphemeSegmentFinder()
+         * @see SegmentFinder
+         * @see SegmentFinder.DefaultSegmentFinder
+         */
+        @NonNull
+        public Builder setGraphemeSegmentFinder(@NonNull SegmentFinder graphemeSegmentFinder) {
+            mGraphemeSegmentFinder = Objects.requireNonNull(graphemeSegmentFinder);
+            return this;
+        }
+
+        /**
+         * Set the {@link SegmentFinder} that locates the word boundaries. <br/>
+         *
+         * Please note that only the word segments within the range from start to end will
+         * be available to the IME. The remaining information will be discarded during serialization
+         * for better performance.
+         * @param wordSegmentFinder set the {@link SegmentFinder} that locates the word boundaries.
+         * @throws NullPointerException if the given {@code wordSegmentFinder} is {@code null}.
+         *
+         * @see #getWordSegmentFinder()
+         * @see SegmentFinder
+         * @see SegmentFinder.DefaultSegmentFinder
+         */
+        @NonNull
+        public Builder setWordSegmentFinder(@NonNull SegmentFinder wordSegmentFinder) {
+            mWordSegmentFinder = Objects.requireNonNull(wordSegmentFinder);
+            return this;
+        }
+
+        /**
+         * Set the {@link SegmentFinder} that locates the line boundaries. Aside from the hard
+         * breaks in the text, it should also locate the soft line breaks added by the editor.
+         * It is expected that the characters within the same line is rendered on the same baseline.
+         * (Except for some text formatted as subscript and superscript.) <br/>
+         *
+         * Please note that only the line segments within the range from start to end will
+         * be available to the IME. The remaining information will be discarded during serialization
+         * for better performance.
+         * @param lineSegmentFinder set the {@link SegmentFinder} that locates the line boundaries.
+         * @throws NullPointerException if the given {@code lineSegmentFinder} is {@code null}.
+         *
+         * @see #getLineSegmentFinder()
+         * @see SegmentFinder
+         * @see SegmentFinder.DefaultSegmentFinder
+         */
+        @NonNull
+        public Builder setLineSegmentFinder(@NonNull SegmentFinder lineSegmentFinder) {
+            mLineSegmentFinder = Objects.requireNonNull(lineSegmentFinder);
+            return this;
+        }
+
+        /**
+         * Create the {@link TextBoundsInfo} using the parameters in this {@link Builder}.
+         *
+         * @throws IllegalStateException in the following conditions:
+         * <ul>
+         *     <li>if the {@code start} or {@code end} is not set.</li>
+         *     <li>if the {@code matrix} is not set.</li>
+         *     <li>if {@code characterBounds} is not set or its length doesn't equal to
+         *     4 * ({@code end} - {@code start}).</li>
+         *     <li>if the {@code characterFlags} is not set or its length doesn't equal to
+         *     ({@code end} - {@code start}).</li>
+         *     <li>if {@code graphemeSegmentFinder}, {@code wordSegmentFinder} or
+         *     {@code lineSegmentFinder} is not set.</li>
+         *     <li>if characters in the same line has inconsistent {@link #FLAG_LINE_IS_RTL}
+         *     flag.</li>
+         * </ul>
+         */
+        @NonNull
+        public TextBoundsInfo build() {
+            if (mStart < 0 || mEnd < 0) {
+                throw new IllegalStateException("Start and end must be set.");
+            }
+
+            if (!mMatrixInitialized) {
+                throw new IllegalStateException("Matrix must be set.");
+            }
+
+            if (mCharacterBounds == null) {
+                throw new IllegalStateException("CharacterBounds must be set.");
+            }
+
+            if (mCharacterFlags == null) {
+                throw new IllegalStateException("CharacterFlags must be set.");
+            }
+
+            if (mCharacterBidiLevels == null) {
+                throw new IllegalStateException("CharacterBidiLevel must be set.");
+            }
+
+            if (mCharacterBounds.length != 4 * (mEnd - mStart)) {
+                throw new IllegalStateException("The length of characterBounds doesn't match the "
+                        + "length of the given start and end."
+                        + " Expected length: " + (4 * (mEnd - mStart))
+                        + " characterBounds length: " + mCharacterBounds.length);
+            }
+            if (mCharacterFlags.length != mEnd - mStart) {
+                throw new IllegalStateException("The length of characterFlags doesn't match the "
+                        + "length of the given start and end."
+                        + " Expected length: " + (mEnd - mStart)
+                        + " characterFlags length: " + mCharacterFlags.length);
+            }
+            if (mCharacterBidiLevels.length != mEnd - mStart) {
+                throw new IllegalStateException("The length of characterBidiLevels doesn't match"
+                        + " the length of the given start and end."
+                        + " Expected length: " + (mEnd - mStart)
+                        + " characterFlags length: " + mCharacterBidiLevels.length);
+            }
+            if (mGraphemeSegmentFinder == null) {
+                throw new IllegalStateException("GraphemeSegmentFinder must be set.");
+            }
+            if (mWordSegmentFinder == null) {
+                throw new IllegalStateException("WordSegmentFinder must be set.");
+            }
+            if (mLineSegmentFinder == null) {
+                throw new IllegalStateException("LineSegmentFinder must be set.");
+            }
+
+            if (!isLineDirectionFlagConsistent(mCharacterFlags, mLineSegmentFinder, mStart, mEnd)) {
+                throw new IllegalStateException("characters in the same line must have the same "
+                        + "FLAG_LINE_IS_RTL flag value.");
+            }
+            return new TextBoundsInfo(this);
+        }
+    }
+
+    /**
+     * Encode the segment start and end positions in {@link SegmentFinder} to a flags array.
+     *
+     * For example:
+     * Text: "A BC DE"
+     * Input:
+     *     start: 2, end: 7                                     // substring "BC DE"
+     *     SegmentFinder: segment ranges = [(2, 4), (5, 7)]     // a word break iterator
+     *     flags: [0x0000, 0x0000, 0x0080, 0x0000, 0x0000, 0x0000] // 0x0080 is whitespace
+     *     segmentStartFlag: 0x0100
+     *     segmentEndFlag: 0x0200
+     * Output:
+     *     flags: [0x0100, 0x0000, 0x0280, 0x0100, 0x0000, 0x0200]
+     *  The index 2 and 5 encode segment starts, the index 4 and 7 encode a segment end.
+     *
+     * @param flags the flags array to receive the results.
+     * @param segmentStartFlag the flag used to encode the segment start.
+     * @param segmentEndFlag the flag used to encode the segment end.
+     * @param start the start index of the encoded range, inclusive.
+     * @param end the end index of the encoded range, inclusive.
+     * @param segmentFinder the SegmentFinder to be encoded.
+     *
+     * @see #decodeSegmentFinder(int[], int, int, int, int)
+     */
+    private static void encodeSegmentFinder(@NonNull int[] flags, int segmentStartFlag,
+            int segmentEndFlag, int start, int end, @NonNull SegmentFinder segmentFinder) {
+        if (end - start + 1 != flags.length) {
+            throw new IllegalStateException("The given flags array must have the same length as"
+                    + " the given range. flags length: " + flags.length
+                    + " range: [" + start + ", " + end + "]");
+        }
+
+        int segmentEnd = segmentFinder.nextEndBoundary(start);
+        if (segmentEnd == SegmentFinder.DONE) return;
+        int segmentStart = segmentFinder.previousStartBoundary(segmentEnd);
+
+        while (segmentEnd != SegmentFinder.DONE && segmentEnd <= end) {
+            if (segmentStart >= start) {
+                flags[segmentStart - start] |= segmentStartFlag;
+                flags[segmentEnd - start] |= segmentEndFlag;
+            }
+            segmentStart = segmentFinder.nextStartBoundary(segmentStart);
+            segmentEnd = segmentFinder.nextEndBoundary(segmentEnd);
+        }
+    }
+
+    /**
+     * Decode a {@link SegmentFinder} from a flags array.
+     *
+     * For example:
+     * Text: "A BC DE"
+     * Input:
+     *     start: 2, end: 7                                     // substring "BC DE"
+     *     flags: [0x0100, 0x0000, 0x0280, 0x0100, 0x0000, 0x0200]
+     *     segmentStartFlag: 0x0100
+     *     segmentEndFlag: 0x0200
+     * Output:
+     *     SegmentFinder: segment ranges = [(2, 4), (5, 7)]
+     *
+     * @param flags the flags array to decode the SegmentFinder.
+     * @param segmentStartFlag the flag to decode a segment start.
+     * @param segmentEndFlag the flag to decode a segment end.
+     * @param start the start index of the interested range, inclusive.
+     * @param end the end index of the interested range, inclusive.
+     *
+     * @see #encodeSegmentFinder(int[], int, int, int, int, SegmentFinder)
+     */
+    private static SegmentFinder decodeSegmentFinder(int[] flags, int segmentStartFlag,
+            int segmentEndFlag, int start, int end) {
+        if (end - start + 1 != flags.length) {
+            throw new IllegalStateException("The given flags array must have the same length as"
+                    + " the given range. flags length: " + flags.length
+                    + " range: [" + start + ", " + end + "]");
+        }
+        int[] breaks = ArrayUtils.newUnpaddedIntArray(10);
+        int count = 0;
+        for (int offset = 0; offset < flags.length; ++offset) {
+            if ((flags[offset] & segmentStartFlag) == segmentStartFlag) {
+                breaks = GrowingArrayUtils.append(breaks, count++, start + offset);
+            }
+            if ((flags[offset] & segmentEndFlag) == segmentEndFlag) {
+                breaks = GrowingArrayUtils.append(breaks, count++, start + offset);
+            }
+        }
+        return new SegmentFinder.DefaultSegmentFinder(Arrays.copyOf(breaks, count));
+    }
+
+    /**
+     * Check whether the {@link #FLAG_LINE_IS_RTL} is the same for characters in the same line.
+     * @return true if all characters in the same line has the same {@link #FLAG_LINE_IS_RTL} flag.
+     */
+    private static boolean isLineDirectionFlagConsistent(int[] characterFlags,
+            SegmentFinder lineSegmentFinder, int start, int end) {
+        int segmentEnd = lineSegmentFinder.nextEndBoundary(start);
+        if (segmentEnd == SegmentFinder.DONE) return true;
+        int segmentStart = lineSegmentFinder.previousStartBoundary(segmentEnd);
+
+        while (segmentStart != SegmentFinder.DONE && segmentStart < end) {
+            final int lineStart = Math.max(segmentStart, start);
+            final int lineEnd = Math.min(segmentEnd, end);
+            final boolean lineIsRtl = (characterFlags[lineStart - start] & FLAG_LINE_IS_RTL) != 0;
+            for (int index = lineStart + 1; index < lineEnd; ++index) {
+                final int flags = characterFlags[index - start];
+                final boolean characterLineIsRtl = (flags & FLAG_LINE_IS_RTL) != 0;
+                if (characterLineIsRtl != lineIsRtl) {
+                    return false;
+                }
+            }
+
+            segmentStart = lineSegmentFinder.nextStartBoundary(segmentStart);
+            segmentEnd = lineSegmentFinder.nextEndBoundary(segmentEnd);
+        }
+        return true;
+    }
+}
diff --git a/core/java/android/view/inputmethod/TextBoundsInfoResult.java b/core/java/android/view/inputmethod/TextBoundsInfoResult.java
new file mode 100644
index 0000000..62df17a
--- /dev/null
+++ b/core/java/android/view/inputmethod/TextBoundsInfoResult.java
@@ -0,0 +1,137 @@
+/*
+ * 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 android.view.inputmethod;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.RectF;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * The object that holds the result of the
+ * {@link InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)} call.
+ *
+ * @see InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)
+ */
+public final class TextBoundsInfoResult {
+    private final int mResultCode;
+    private final TextBoundsInfo mTextBoundsInfo;
+
+    /**
+     * Result for {@link InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)} when the
+     * editor doesn't implement the method.
+     */
+    public static final int CODE_UNSUPPORTED = 0;
+
+    /**
+     * Result for {@link InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)} when the
+     * editor successfully returns a {@link TextBoundsInfo}.
+     */
+    public static final int CODE_SUCCESS = 1;
+
+    /**
+     * Result for {@link InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)} when the
+     * request failed. This result code is returned when the editor can't provide a valid
+     * {@link TextBoundsInfo}. (e.g. The editor view is not laid out.)
+     */
+    public static final int CODE_FAILED = 2;
+
+    /**
+     * Result for {@link InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)} when the
+     * request is cancelled. This happens when the {@link InputConnection} is or becomes
+     * invalidated while requesting the
+     * {@link TextBoundsInfo}, for example because a new {@code InputConnection} was started, or
+     * due to {@link InputMethodManager#invalidateInput}.
+     */
+    public static final int CODE_CANCELLED = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "CODE_" }, value = {
+            CODE_UNSUPPORTED,
+            CODE_SUCCESS,
+            CODE_FAILED,
+            CODE_CANCELLED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ResultCode {}
+
+    /**
+     * Create a {@link TextBoundsInfoResult} object with no {@link TextBoundsInfo}.
+     * The given {@code resultCode} can't be {@link #CODE_SUCCESS}.
+     * @param resultCode the result code of the
+     * {@link InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)} call.
+     */
+    public TextBoundsInfoResult(@ResultCode int resultCode) {
+        this(resultCode, null);
+    }
+
+    /**
+     * Create a {@link TextBoundsInfoResult} object.
+     *
+     * @param resultCode the result code of the
+     * {@link InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)} call.
+     * @param textBoundsInfo the returned {@link TextBoundsInfo} of the
+     * {@link InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)} call. It can't be
+     *                       null if the {@code resultCode} is {@link #CODE_SUCCESS}.
+     *
+     * @throws IllegalStateException if the resultCode is
+     * {@link #CODE_SUCCESS} but the given {@code textBoundsInfo}
+     * is null.
+     */
+    public TextBoundsInfoResult(@ResultCode int resultCode,
+            @NonNull TextBoundsInfo textBoundsInfo) {
+        if (resultCode == CODE_SUCCESS && textBoundsInfo == null) {
+            throw new IllegalStateException("TextBoundsInfo must be provided when the resultCode "
+                    + "is CODE_SUCCESS.");
+        }
+        mResultCode = resultCode;
+        mTextBoundsInfo = textBoundsInfo;
+    }
+
+    /**
+     * Return the result code of the
+     * {@link InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)} call.
+     * Its value is one of the {@link #CODE_UNSUPPORTED}, {@link #CODE_SUCCESS},
+     * {@link #CODE_FAILED} and {@link #CODE_CANCELLED}.
+     */
+    @ResultCode
+    public int getResultCode() {
+        return mResultCode;
+    }
+
+    /**
+     * Return the {@link TextBoundsInfo} provided by the editor. It is non-null if the
+     * {@code resultCode} is {@link #CODE_SUCCESS}.
+     * Otherwise, it can be null in the following conditions:
+     * <ul>
+     *    <li>the editor doesn't support
+     *      {@link InputConnection#requestTextBoundsInfo(RectF, Executor, Consumer)}.</li>
+     *    <li>the editor doesn't have the text bounds information at the moment. (e.g. the editor
+     *    view is not laid out yet.) </li>
+     *    <li> the {@link InputConnection} is or become inactive during the request. </li>
+     * <ul/>
+     */
+    @Nullable
+    public TextBoundsInfo getTextBoundsInfo() {
+        return  mTextBoundsInfo;
+    }
+}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index b5c58fb..1144b59 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -69,6 +69,7 @@
 import android.graphics.BlendMode;
 import android.graphics.Canvas;
 import android.graphics.Insets;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Paint.FontMetricsInt;
 import android.graphics.Path;
@@ -200,6 +201,7 @@
 import android.view.inputmethod.RemoveSpaceGesture;
 import android.view.inputmethod.SelectGesture;
 import android.view.inputmethod.SelectRangeGesture;
+import android.view.inputmethod.TextBoundsInfo;
 import android.view.inspector.InspectableProperty;
 import android.view.inspector.InspectableProperty.EnumEntry;
 import android.view.inspector.InspectableProperty.FlagEntry;
@@ -12953,18 +12955,15 @@
         getLocalVisibleRect(rect);
         final RectF visibleRect = new RectF(rect);
 
-        final float[] characterBounds = new float[4 * (endIndex - startIndex)];
-        mLayout.fillCharacterBounds(startIndex, endIndex, characterBounds, 0);
+
+        final float[] characterBounds = getCharacterBounds(startIndex, endIndex,
+                viewportToContentHorizontalOffset, viewportToContentVerticalOffset);
         final int limit = endIndex - startIndex;
         for (int offset = 0; offset < limit; ++offset) {
-            final float left =
-                    characterBounds[offset * 4] + viewportToContentHorizontalOffset;
-            final float top =
-                    characterBounds[offset * 4 + 1] + viewportToContentVerticalOffset;
-            final float right =
-                    characterBounds[offset * 4 + 2] + viewportToContentHorizontalOffset;
-            final float bottom =
-                    characterBounds[offset * 4 + 3] + viewportToContentVerticalOffset;
+            final float left = characterBounds[offset * 4];
+            final float top = characterBounds[offset * 4 + 1];
+            final float right = characterBounds[offset * 4 + 2];
+            final float bottom = characterBounds[offset * 4 + 3];
 
             final boolean hasVisibleRegion = visibleRect.intersects(left, top, right, bottom);
             final boolean hasInVisibleRegion = !visibleRect.contains(left, top, right, bottom);
@@ -12985,6 +12984,149 @@
     }
 
     /**
+     * Return the bounds of the characters in the given range, in TextView's coordinates.
+     *
+     * @param start the start index of the interested text range, inclusive.
+     * @param end the end index of the interested text range, exclusive.
+     * @param layoutLeft the left of the given {@code layout} in the editor view's coordinates.
+     * @param layoutTop  the top of the given {@code layout} in the editor view's coordinates.
+     * @return the character bounds stored in a flattened array, in the editor view's coordinates.
+     */
+    private float[] getCharacterBounds(int start, int end, float layoutLeft, float layoutTop) {
+        final float[] characterBounds = new float[4 * (end - start)];
+        mLayout.fillCharacterBounds(start, end, characterBounds, 0);
+        for (int offset = 0; offset < end - start; ++offset) {
+            characterBounds[4 * offset] += layoutLeft;
+            characterBounds[4 * offset + 1] += layoutTop;
+            characterBounds[4 * offset + 2] += layoutLeft;
+            characterBounds[4 * offset + 3] += layoutTop;
+        }
+        return characterBounds;
+    }
+
+    /**
+     * Creates the {@link TextBoundsInfo} for the text lines that intersects with the {@code rectF}.
+     * @hide
+     */
+    public TextBoundsInfo getTextBoundsInfo(@NonNull RectF rectF) {
+        final Layout layout = getLayout();
+        if (layout == null) {
+            // No valid text layout, return null.
+            return null;
+        }
+        final CharSequence text = layout.getText();
+        if (text == null) {
+            // It's impossible that a layout has no text. Check here to avoid NPE.
+            return null;
+        }
+
+        final Matrix localToGlobalMatrix = new Matrix();
+        transformMatrixToGlobal(localToGlobalMatrix);
+        final Matrix globalToLocalMatrix = new Matrix();
+        if (!localToGlobalMatrix.invert(globalToLocalMatrix)) {
+            // Can't map global rectF to local coordinates, this is almost impossible in practice.
+            return null;
+        }
+
+        final float layoutLeft = viewportToContentHorizontalOffset();
+        final float layoutTop = viewportToContentVerticalOffset();
+
+        final RectF localRectF = new RectF(rectF);
+        globalToLocalMatrix.mapRect(localRectF);
+        localRectF.offset(-layoutLeft, -layoutTop);
+
+        // Text length is 0. There is no character bounds, return empty TextBoundsInfo.
+        // rectF doesn't intersect with the layout, return empty TextBoundsInfo.
+        if (!localRectF.intersects(0f, 0f, layout.getWidth(), layout.getHeight())
+                || text.length() == 0) {
+            final TextBoundsInfo.Builder builder = new TextBoundsInfo.Builder();
+            final SegmentFinder emptySegmentFinder =
+                    new SegmentFinder.DefaultSegmentFinder(new int[0]);
+            builder.setStartAndEnd(0, 0)
+                    .setMatrix(localToGlobalMatrix)
+                    .setCharacterBounds(new float[0])
+                    .setCharacterBidiLevel(new int[0])
+                    .setCharacterFlags(new int[0])
+                    .setGraphemeSegmentFinder(emptySegmentFinder)
+                    .setLineSegmentFinder(emptySegmentFinder)
+                    .setWordSegmentFinder(emptySegmentFinder);
+            return  builder.build();
+        }
+
+        final int startLine = layout.getLineForVertical((int) Math.floor(localRectF.top));
+        final int endLine = layout.getLineForVertical((int) Math.floor(localRectF.bottom));
+        final int start = layout.getLineStart(startLine);
+        final int end = layout.getLineEnd(endLine);
+
+        // Compute character bounds.
+        final float[] characterBounds = getCharacterBounds(start, end, layoutLeft, layoutTop);
+
+        // Compute character flags and BiDi levels.
+        final int[] characterFlags = new int[end - start];
+        final int[] characterBidiLevels = new int[end - start];
+        for (int line = startLine; line <= endLine; ++line) {
+            final int lineStart = layout.getLineStart(line);
+            final int lineEnd = layout.getLineEnd(line);
+            final Layout.Directions directions = layout.getLineDirections(line);
+            for (int i = 0; i < directions.getRunCount(); ++i) {
+                final int runStart = directions.getRunStart(i) + lineStart;
+                final int runEnd = Math.min(runStart + directions.getRunLength(i), lineEnd);
+                final int runLevel = directions.getRunLevel(i);
+                Arrays.fill(characterBidiLevels, runStart - start, runEnd - start, runLevel);
+            }
+
+            final boolean lineIsRtl =
+                    layout.getParagraphDirection(line) == Layout.DIR_RIGHT_TO_LEFT;
+            for (int index = lineStart; index < lineEnd; ++index) {
+                int flags = 0;
+                if (TextUtils.isWhitespace(text.charAt(index))) {
+                    flags |= TextBoundsInfo.FLAG_CHARACTER_WHITESPACE;
+                }
+                if (TextUtils.isPunctuation(Character.codePointAt(text, index))) {
+                    flags |= TextBoundsInfo.FLAG_CHARACTER_PUNCTUATION;
+                }
+                if (TextUtils.isNewline(Character.codePointAt(text, index))) {
+                    flags |= TextBoundsInfo.FLAG_CHARACTER_LINEFEED;
+                }
+                if (lineIsRtl) {
+                    flags |= TextBoundsInfo.FLAG_LINE_IS_RTL;
+                }
+                characterFlags[index - start] = flags;
+            }
+        }
+
+        // Create grapheme SegmentFinder.
+        final SegmentFinder graphemeSegmentFinder =
+                new GraphemeClusterSegmentFinder(text, layout.getPaint());
+
+        // Create word SegmentFinder.
+        final WordIterator wordIterator = getWordIterator();
+        wordIterator.setCharSequence(text, 0, text.length());
+        final SegmentFinder wordSegmentFinder = new WordSegmentFinder(text, wordIterator);
+
+        // Create line SegmentFinder.
+        final int lineCount = endLine - startLine + 1;
+        final int[] lineRanges = new int[2 * lineCount];
+        for (int line = startLine; line <= endLine; ++line) {
+            final int offset = line - startLine;
+            lineRanges[2 * offset] = layout.getLineStart(line);
+            lineRanges[2 * offset + 1] = layout.getLineEnd(line);
+        }
+        final SegmentFinder lineSegmentFinder = new SegmentFinder.DefaultSegmentFinder(lineRanges);
+
+        final TextBoundsInfo.Builder builder = new TextBoundsInfo.Builder();
+        builder.setStartAndEnd(start, end)
+                .setMatrix(localToGlobalMatrix)
+                .setCharacterBounds(characterBounds)
+                .setCharacterBidiLevel(characterBidiLevels)
+                .setCharacterFlags(characterFlags)
+                .setGraphemeSegmentFinder(graphemeSegmentFinder)
+                .setLineSegmentFinder(lineSegmentFinder)
+                .setWordSegmentFinder(wordSegmentFinder);
+        return  builder.build();
+    }
+
+    /**
      * @hide
      */
     public boolean isPositionVisible(final float positionX, final float positionY) {
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 8815ab3..0956a71 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -141,6 +141,10 @@
     /** The first unused bit. This can be used by remotes to attach custom flags to this change. */
     public static final int FLAG_FIRST_CUSTOM = 1 << 17;
 
+    /** The change belongs to a window that won't contain activities. */
+    public static final int FLAGS_IS_NON_APP_WINDOW =
+            FLAG_IS_WALLPAPER | FLAG_IS_INPUT_METHOD | FLAG_IS_SYSTEM_WINDOW;
+
     /** @hide */
     @IntDef(prefix = { "FLAG_" }, value = {
             FLAG_NONE,
@@ -579,11 +583,16 @@
             return mFlags;
         }
 
-        /** Whether the given change flags has included in this change. */
+        /** Whether this change contains any of the given change flags. */
         public boolean hasFlags(@ChangeFlags int flags) {
             return (mFlags & flags) != 0;
         }
 
+        /** Whether this change contains all of the given change flags. */
+        public boolean hasAllFlags(@ChangeFlags int flags) {
+            return (mFlags & flags) == flags;
+        }
+
         /**
          * @return the bounds of the container before the change. It may be empty if the container
          * is coming into existence.
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index f600c36..b155dd4 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -25,6 +25,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.RectF;
 import android.os.Bundle;
 import android.text.Editable;
 import android.text.Selection;
@@ -46,9 +47,12 @@
 import android.view.inputmethod.RemoveSpaceGesture;
 import android.view.inputmethod.SelectGesture;
 import android.view.inputmethod.SelectRangeGesture;
+import android.view.inputmethod.TextBoundsInfo;
+import android.view.inputmethod.TextBoundsInfoResult;
 import android.widget.TextView;
 
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 
 /**
@@ -262,6 +266,23 @@
     }
 
     @Override
+    public void requestTextBoundsInfo(
+            @NonNull RectF rectF, @Nullable @CallbackExecutor Executor executor,
+            @NonNull Consumer<TextBoundsInfoResult> consumer) {
+        final TextBoundsInfo textBoundsInfo = mTextView.getTextBoundsInfo(rectF);
+        final int resultCode;
+        if (textBoundsInfo != null) {
+            resultCode = TextBoundsInfoResult.CODE_SUCCESS;
+        } else {
+            resultCode = TextBoundsInfoResult.CODE_FAILED;
+        }
+        final TextBoundsInfoResult textBoundsInfoResult =
+                new TextBoundsInfoResult(resultCode, textBoundsInfo);
+
+        executor.execute(() -> consumer.accept(textBoundsInfoResult));
+    }
+
+    @Override
     public boolean setImeConsumesInput(boolean imeConsumesInput) {
         if (mTextView == null) {
             return super.setImeConsumesInput(imeConsumesInput);
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodManagerGlobal.java b/core/java/com/android/internal/inputmethod/IInputMethodManagerGlobal.java
deleted file mode 100644
index 5392bdc..0000000
--- a/core/java/com/android/internal/inputmethod/IInputMethodManagerGlobal.java
+++ /dev/null
@@ -1,175 +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.internal.inputmethod;
-
-import android.annotation.AnyThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-
-import com.android.internal.view.IInputMethodManager;
-
-import java.util.function.Consumer;
-
-/**
- * A global wrapper to directly invoke {@link IInputMethodManager} IPCs.
- *
- * <p>All public static methods are guaranteed to be thread-safe.</p>
- *
- * <p>All public methods are guaranteed to do nothing when {@link IInputMethodManager} is
- * unavailable.</p>
- */
-public final class IInputMethodManagerGlobal {
-    @Nullable
-    private static volatile IInputMethodManager sServiceCache = null;
-
-    /**
-     * @return {@code true} if {@link IInputMethodManager} is available.
-     */
-    @AnyThread
-    public static boolean isAvailable() {
-        return getService() != null;
-    }
-
-    @AnyThread
-    @Nullable
-    private static IInputMethodManager getService() {
-        IInputMethodManager service = sServiceCache;
-        if (service == null) {
-            service = IInputMethodManager.Stub.asInterface(
-                    ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
-            if (service == null) {
-                return null;
-            }
-            sServiceCache = service;
-        }
-        return service;
-    }
-
-    @AnyThread
-    private static void handleRemoteExceptionOrRethrow(@NonNull RemoteException e,
-            @Nullable Consumer<RemoteException> exceptionHandler) {
-        if (exceptionHandler != null) {
-            exceptionHandler.accept(e);
-        } else {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Invokes {@link IInputMethodManager#startProtoDump(byte[], int, String)}.
-     *
-     * @param protoDump client or service side information to be stored by the server
-     * @param source where the information is coming from, refer to
-     *               {@link ImeTracing#IME_TRACING_FROM_CLIENT} and
-     *               {@link ImeTracing#IME_TRACING_FROM_IMS}
-     * @param where where the information is coming from.
-     * @param exceptionHandler an optional {@link RemoteException} handler.
-     */
-    @RequiresNoPermission
-    @AnyThread
-    public static void startProtoDump(byte[] protoDump, int source, String where,
-            @Nullable Consumer<RemoteException> exceptionHandler) {
-        final IInputMethodManager service = getService();
-        if (service == null) {
-            return;
-        }
-        try {
-            service.startProtoDump(protoDump, source, where);
-        } catch (RemoteException e) {
-            handleRemoteExceptionOrRethrow(e, exceptionHandler);
-        }
-    }
-
-    /**
-     * Invokes {@link IInputMethodManager#startImeTrace()}.
-     *
-     * @param exceptionHandler an optional {@link RemoteException} handler.
-     */
-    @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
-    @AnyThread
-    public static void startImeTrace(@Nullable Consumer<RemoteException> exceptionHandler) {
-        final IInputMethodManager service = getService();
-        if (service == null) {
-            return;
-        }
-        try {
-            service.startImeTrace();
-        } catch (RemoteException e) {
-            handleRemoteExceptionOrRethrow(e, exceptionHandler);
-        }
-    }
-
-    /**
-     * Invokes {@link IInputMethodManager#stopImeTrace()}.
-     *
-     * @param exceptionHandler an optional {@link RemoteException} handler.
-     */
-    @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
-    @AnyThread
-    public static void stopImeTrace(@Nullable Consumer<RemoteException> exceptionHandler) {
-        final IInputMethodManager service = getService();
-        if (service == null) {
-            return;
-        }
-        try {
-            service.stopImeTrace();
-        } catch (RemoteException e) {
-            handleRemoteExceptionOrRethrow(e, exceptionHandler);
-        }
-    }
-
-    /**
-     * Invokes {@link IInputMethodManager#isImeTraceEnabled()}.
-     *
-     * @return The return value of {@link IInputMethodManager#isImeTraceEnabled()}.
-     */
-    @RequiresNoPermission
-    @AnyThread
-    public static boolean isImeTraceEnabled() {
-        final IInputMethodManager service = getService();
-        if (service == null) {
-            return false;
-        }
-        try {
-            return service.isImeTraceEnabled();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Invokes {@link IInputMethodManager#removeImeSurface()}
-     */
-    @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW)
-    @AnyThread
-    public static void removeImeSurface(@Nullable Consumer<RemoteException> exceptionHandler) {
-        final IInputMethodManager service = getService();
-        if (service == null) {
-            return;
-        }
-        try {
-            service.removeImeSurface();
-        } catch (RemoteException e) {
-            handleRemoteExceptionOrRethrow(e, exceptionHandler);
-        }
-    }
-}
diff --git a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
index ea5c9a3..81e060d 100644
--- a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
+++ b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
@@ -16,20 +16,15 @@
 
 package com.android.internal.inputmethod;
 
+import android.graphics.RectF;
 import android.os.Bundle;
 import android.os.ResultReceiver;
 import android.view.KeyEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
-import android.view.inputmethod.DeleteGesture;
-import android.view.inputmethod.DeleteRangeGesture;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputContentInfo;
-import android.view.inputmethod.InsertGesture;
-import android.view.inputmethod.JoinOrSplitGesture;
-import android.view.inputmethod.RemoveSpaceGesture;
-import android.view.inputmethod.SelectGesture;
-import android.view.inputmethod.SelectRangeGesture;
+import android.view.inputmethod.ParcelableHandwritingGesture;
 import android.view.inputmethod.TextAttribute;
 
 import com.android.internal.infra.AndroidFuture;
@@ -94,26 +89,8 @@
     void performPrivateCommand(in InputConnectionCommandHeader header, String action,
             in Bundle data);
 
-    void performHandwritingSelectGesture(in InputConnectionCommandHeader header,
-            in SelectGesture gesture, in ResultReceiver resultReceiver);
-
-    void performHandwritingSelectRangeGesture(in InputConnectionCommandHeader header,
-            in SelectRangeGesture gesture, in ResultReceiver resultReceiver);
-
-    void performHandwritingInsertGesture(in InputConnectionCommandHeader header,
-            in InsertGesture gesture, in ResultReceiver resultReceiver);
-
-    void performHandwritingDeleteGesture(in InputConnectionCommandHeader header,
-            in DeleteGesture gesture, in ResultReceiver resultReceiver);
-
-    void performHandwritingDeleteRangeGesture(in InputConnectionCommandHeader header,
-                in DeleteRangeGesture gesture, in ResultReceiver resultReceiver);
-
-    void performHandwritingRemoveSpaceGesture(in InputConnectionCommandHeader header,
-            in RemoveSpaceGesture gesture, in ResultReceiver resultReceiver);
-
-    void performHandwritingJoinOrSplitGesture(in InputConnectionCommandHeader header,
-            in JoinOrSplitGesture gesture, in ResultReceiver resultReceiver);
+    void performHandwritingGesture(in InputConnectionCommandHeader header,
+            in ParcelableHandwritingGesture gesture, in ResultReceiver resultReceiver);
 
     void setComposingRegion(in InputConnectionCommandHeader header, int start, int end);
 
@@ -130,6 +107,9 @@
                 int cursorUpdateMode, int cursorUpdateFilter, int imeDisplayId,
                  in AndroidFuture future /* T=Boolean */);
 
+    void requestTextBoundsInfo(in InputConnectionCommandHeader header, in RectF rect,
+           in ResultReceiver resultReceiver /* T=TextBoundsInfoResult */);
+
     void commitContent(in InputConnectionCommandHeader header, in InputContentInfo inputContentInfo,
             int flags, in Bundle opts, in AndroidFuture future /* T=Boolean */);
 
diff --git a/core/java/com/android/internal/inputmethod/ImeTracing.java b/core/java/com/android/internal/inputmethod/ImeTracing.java
index a4328cc..e6a9b54 100644
--- a/core/java/com/android/internal/inputmethod/ImeTracing.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracing.java
@@ -22,6 +22,7 @@
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodManagerGlobal;
 
 import java.io.PrintWriter;
 
@@ -44,7 +45,7 @@
     private static ImeTracing sInstance;
     static boolean sEnabled = false;
 
-    private final boolean mIsAvailable = IInputMethodManagerGlobal.isAvailable();
+    private final boolean mIsAvailable = InputMethodManagerGlobal.isImeTraceAvailable();
 
     protected boolean mDumpInProgress;
     protected final Object mDumpInProgressLock = new Object();
@@ -81,7 +82,7 @@
      * @param where
      */
     public void sendToService(byte[] protoDump, int source, String where) {
-        IInputMethodManagerGlobal.startProtoDump(protoDump, source, where,
+        InputMethodManagerGlobal.startProtoDump(protoDump, source, where,
                 e -> Log.e(TAG, "Exception while sending ime-related dump to server", e));
     }
 
@@ -90,7 +91,7 @@
      */
     @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
     public final void startImeTrace() {
-        IInputMethodManagerGlobal.startImeTrace(e -> Log.e(TAG, "Could not start ime trace.", e));
+        InputMethodManagerGlobal.startImeTrace(e -> Log.e(TAG, "Could not start ime trace.", e));
     }
 
     /**
@@ -98,7 +99,7 @@
      */
     @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
     public final void stopImeTrace() {
-        IInputMethodManagerGlobal.stopImeTrace(e -> Log.e(TAG, "Could not stop ime trace.", e));
+        InputMethodManagerGlobal.stopImeTrace(e -> Log.e(TAG, "Could not stop ime trace.", e));
     }
 
     /**
diff --git a/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java
index 4caca84..95ed4ed 100644
--- a/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.util.proto.ProtoOutputStream;
 import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodManagerGlobal;
 
 import java.io.PrintWriter;
 
@@ -28,7 +29,7 @@
  */
 class ImeTracingClientImpl extends ImeTracing {
     ImeTracingClientImpl() {
-        sEnabled = IInputMethodManagerGlobal.isImeTraceEnabled();
+        sEnabled = InputMethodManagerGlobal.isImeTraceEnabled();
     }
 
     @Override
diff --git a/core/java/com/android/internal/security/TEST_MAPPING b/core/java/com/android/internal/security/TEST_MAPPING
index 9a5e90e..803760c 100644
--- a/core/java/com/android/internal/security/TEST_MAPPING
+++ b/core/java/com/android/internal/security/TEST_MAPPING
@@ -1,6 +1,17 @@
 {
   "presubmit": [
     {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "com.android.internal.security."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        }
+      ]
+    },
+    {
       "name": "ApkVerityTest",
       "file_patterns": ["VerityUtils\\.java"]
     }
diff --git a/core/java/com/android/internal/security/VerityUtils.java b/core/java/com/android/internal/security/VerityUtils.java
index cb5820f..7f45c09 100644
--- a/core/java/com/android/internal/security/VerityUtils.java
+++ b/core/java/com/android/internal/security/VerityUtils.java
@@ -23,10 +23,28 @@
 import android.system.OsConstants;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.internal.org.bouncycastle.cms.CMSException;
+import com.android.internal.org.bouncycastle.cms.CMSProcessableByteArray;
+import com.android.internal.org.bouncycastle.cms.CMSSignedData;
+import com.android.internal.org.bouncycastle.cms.SignerInformation;
+import com.android.internal.org.bouncycastle.cms.SignerInformationVerifier;
+import com.android.internal.org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import com.android.internal.org.bouncycastle.operator.OperatorCreationException;
+
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
 
 /** Provides fsverity related operations. */
 public abstract class VerityUtils {
@@ -91,6 +109,91 @@
     }
 
     /**
+     * Verifies the signature over the fs-verity digest using the provided certificate.
+     *
+     * This method should only be used by any existing fs-verity use cases that require
+     * PKCS#7 signature verification, if backward compatibility is necessary.
+     *
+     * Since PKCS#7 is too flexible, for the current specific need, only specific configuration
+     * will be accepted:
+     * <ul>
+     *   <li>Must use SHA256 as the digest algorithm
+     *   <li>Must use rsaEncryption as signature algorithm
+     *   <li>Must be detached / without content
+     *   <li>Must not include any signed or unsigned attributes
+     * </ul>
+     *
+     * It is up to the caller to provide an appropriate/trusted certificate.
+     *
+     * @param signatureBlock byte array of a PKCS#7 detached signature
+     * @param digest fs-verity digest with the common configuration using sha256
+     * @param derCertInputStream an input stream of a X.509 certificate in DER
+     * @return whether the verification succeeds
+     */
+    public static boolean verifyPkcs7DetachedSignature(@NonNull byte[] signatureBlock,
+            @NonNull byte[] digest, @NonNull InputStream derCertInputStream) {
+        if (digest.length != 32) {
+            Slog.w(TAG, "Only sha256 is currently supported");
+            return false;
+        }
+
+        try {
+            CMSSignedData signedData = new CMSSignedData(
+                    new CMSProcessableByteArray(toFormattedDigest(digest)),
+                    signatureBlock);
+
+            if (!signedData.isDetachedSignature()) {
+                Slog.w(TAG, "Expect only detached siganture");
+                return false;
+            }
+            if (!signedData.getCertificates().getMatches(null).isEmpty()) {
+                Slog.w(TAG, "Expect no certificate in signature");
+                return false;
+            }
+            if (!signedData.getCRLs().getMatches(null).isEmpty()) {
+                Slog.w(TAG, "Expect no CRL in signature");
+                return false;
+            }
+
+            X509Certificate trustedCert = (X509Certificate) CertificateFactory.getInstance("X.509")
+                    .generateCertificate(derCertInputStream);
+            SignerInformationVerifier verifier = new JcaSimpleSignerInfoVerifierBuilder()
+                    .build(trustedCert);
+
+            // Verify any signature with the trusted certificate.
+            for (SignerInformation si : signedData.getSignerInfos().getSigners()) {
+                // To be the most strict while dealing with the complicated PKCS#7 signature, reject
+                // everything we don't need.
+                if (si.getSignedAttributes() != null && si.getSignedAttributes().size() > 0) {
+                    Slog.w(TAG, "Unexpected signed attributes");
+                    return false;
+                }
+                if (si.getUnsignedAttributes() != null && si.getUnsignedAttributes().size() > 0) {
+                    Slog.w(TAG, "Unexpected unsigned attributes");
+                    return false;
+                }
+                if (!NISTObjectIdentifiers.id_sha256.getId().equals(si.getDigestAlgOID())) {
+                    Slog.w(TAG, "Unsupported digest algorithm OID: " + si.getDigestAlgOID());
+                    return false;
+                }
+                if (!PKCSObjectIdentifiers.rsaEncryption.getId().equals(si.getEncryptionAlgOID())) {
+                    Slog.w(TAG, "Unsupported encryption algorithm OID: "
+                            + si.getEncryptionAlgOID());
+                    return false;
+                }
+
+                if (si.verify(verifier)) {
+                    return true;
+                }
+            }
+            return false;
+        } catch (CertificateException | CMSException | OperatorCreationException e) {
+            Slog.w(TAG, "Error occurred during the PKCS#7 signature verification", e);
+        }
+        return false;
+    }
+
+    /**
      * Returns fs-verity digest for the file if enabled, otherwise returns null. The digest is a
      * hash of root hash of fs-verity's Merkle tree with extra metadata.
      *
@@ -110,6 +213,19 @@
         return result;
     }
 
+    /** @hide */
+    @VisibleForTesting
+    public static byte[] toFormattedDigest(byte[] digest) {
+        // Construct fsverity_formatted_digest used in fs-verity's built-in signature verification.
+        ByteBuffer buffer = ByteBuffer.allocate(12 + digest.length); // struct size + sha256 size
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        buffer.put("FSVerity".getBytes(StandardCharsets.US_ASCII));
+        buffer.putShort((short) 1); // FS_VERITY_HASH_ALG_SHA256
+        buffer.putShort((short) digest.length);
+        buffer.put(digest);
+        return buffer.array();
+    }
+
     private static native int enableFsverityNative(@NonNull String filePath,
             @NonNull byte[] pkcs7Signature);
     private static native int measureFsverityNative(@NonNull String filePath,
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index d59a51a..c50abb3 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -329,7 +329,6 @@
             header_libs: [
                 "bionic_libc_platform_headers",
                 "dnsproxyd_protocol_headers",
-                "libandroid_runtime_vm_headers",
             ],
         },
         host: {
@@ -418,24 +417,3 @@
         never: true,
     },
 }
-
-cc_library_headers {
-    name: "libandroid_runtime_vm_headers",
-    host_supported: true,
-    vendor_available: true,
-    // TODO(b/153609531): remove when libbinder is not native_bridge_supported
-    native_bridge_supported: true,
-    // Allow only modules from the following list to create threads that can be
-    // attached to the JVM. This list should be a subset of the dependencies of
-    // libandroid_runtime.
-    visibility: [
-        "//frameworks/native/libs/binder",
-    ],
-    export_include_dirs: ["include_vm"],
-    header_libs: [
-        "jni_headers",
-    ],
-    export_header_lib_headers: [
-        "jni_headers",
-    ],
-}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 422bdc9..9da28a3 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -22,7 +22,6 @@
 #include <android-base/properties.h>
 #include <android/graphics/jni_runtime.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/vm.h>
 #include <assert.h>
 #include <binder/IBinder.h>
 #include <binder/IPCThreadState.h>
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 1f64df4..4d8dac1 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -116,6 +116,11 @@
     }
 }
 
+static jboolean android_os_Parcel_isForRpc(jlong nativePtr) {
+    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+    return parcel ? parcel->isForRpc() : false;
+}
+
 static jint android_os_Parcel_dataSize(jlong nativePtr)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
@@ -808,6 +813,8 @@
     // @FastNative
     {"nativeMarkForBinder",       "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_markForBinder},
     // @CriticalNative
+    {"nativeIsForRpc",            "(J)Z", (void*)android_os_Parcel_isForRpc},
+    // @CriticalNative
     {"nativeDataSize",            "(J)I", (void*)android_os_Parcel_dataSize},
     // @CriticalNative
     {"nativeDataAvail",           "(J)I", (void*)android_os_Parcel_dataAvail},
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 39ec037..b2994f4 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -70,6 +70,8 @@
                                           deviceInfo.hasMic(), deviceInfo.hasButtonUnderPad(),
                                           deviceInfo.hasSensor(), deviceInfo.hasBattery(),
                                           deviceInfo.supportsUsi()));
+    // Note: We do not populate the Bluetooth address into the InputDevice object to avoid leaking
+    // it to apps that do not have the Bluetooth permission.
 
     const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
     for (const InputDeviceInfo::MotionRange& range: ranges) {
diff --git a/core/jni/include_vm/android_runtime/vm.h b/core/jni/include_vm/android_runtime/vm.h
deleted file mode 100644
index a6e7c16..0000000
--- a/core/jni/include_vm/android_runtime/vm.h
+++ /dev/null
@@ -1,24 +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.
- */
-
-#pragma once
-
-#include <jni.h>
-
-// Get the Java VM. If the symbol doesn't exist at runtime, it means libandroid_runtime
-// is not loaded in the current process. If the symbol exists but it returns nullptr, it
-// means JavaVM is not yet started.
-extern "C" JavaVM* AndroidRuntimeGetJavaVM();
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 9e4f63c..4650000 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -978,6 +978,11 @@
     }
     repeated UserProfile user_profile_group_ids = 4;
     repeated int32 visible_users_array = 5;
+
+    // current_user contains the id of the current user, while current_profiles contains the ids of
+    // both the current user and its profiles (if any)
+    optional int32 current_user = 6;
+    repeated int32 current_profiles = 7;
 }
 
 // sync with com.android.server.am.AppTimeTracker.java
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 554b153..953ea89 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3152,6 +3152,12 @@
     <permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"
                 android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier|role"/>
 
+    <!-- Allows an application to hint that a broadcast is associated with an
+         "interactive" usage scenario
+         @hide -->
+    <permission android:name="android.permission.BROADCAST_OPTION_INTERACTIVE"
+                android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Must be required by activities that handle the intent action
          {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that
          hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system.
@@ -6622,6 +6628,15 @@
     <permission android:name="android.permission.MANAGE_DEVICE_LOCK_STATE"
                 android:protectionLevel="internal|role" />
 
+    <!-- Allows applications to use the long running jobs APIs.
+         <p>This is a special access permission that can be revoked by the system or the user.
+         <p>Apps need to target API {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or above
+         to be able to request this permission.
+         <p>Protection level: appop
+     -->
+    <permission android:name="android.permission.RUN_LONG_JOBS"
+                android:protectionLevel="normal|appop"/>
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 0ebce40..b20bfef 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Beller-ID se verstek is nie beperk nie. Volgende oproep: nie beperk nie"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Diens nie verskaf nie."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Jy kan nie die beller-ID-instelling verander nie."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Geen mobiele datadiens nie"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Noodoproepe is onbeskikbaar"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Geen stemdiens nie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index a861f3c..2574fd8 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"የደዋይ ID ነባሪዎች ወደአልተከለከለም። ቀጥሎ ጥሪ፡አልተከለከለም"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"አገልግሎት አልቀረበም።"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"የደዋይ መታወቂያ ቅንብሮች  መለወጥ አትችልም፡፡"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ምንም የተንቀሳቃሽ ስልክ ውሂብ አገልግሎት የለም"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"የድንገተኛ አደጋ ጥሪ አይገኝም"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ምንም የድምፅ ጥሪ አገልግሎት የለም"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 7623fb1..ba3ebb1 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -76,6 +76,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"الإعداد التلقائي لمعرف المتصل هو غير محظور  . الاتصال التالي: غير محظور"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"الخدمة غير متوفرة."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"لا يمكنك تغيير إعداد معرّف المتصل."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"لا تتوفّر خدمة بيانات جوّال."</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"لا تتوفّر مكالمات طوارئ."</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"لا تتوفر خدمة صوتية"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 3a97baf..d49d3b1 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"কলাৰ আইডি সীমিত নকৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হোৱা নাই"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"সুবিধা যোগান ধৰা হোৱা নাই।"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"আপুনি কলাৰ আইডি ছেটিং সলনি কৰিব নোৱাৰে।"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"কোনো ম’বাইল ডেটা সেৱা নাই"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"জৰুৰীকালীন কল কৰাৰ সুবিধা উপলব্ধ নহয়"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"কোনো ভইচ সেৱা নাই"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 0b361ac..ffd1480 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zəng edənin kimliyi defolt olaraq qadağan deyil. Növbəti zəng: Qadağan deyil"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Xidmət təmin edilməyib."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Çağrı kimliyi ayarını dəyişə bilməzsiniz."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Mobil data xidməti yoxdur"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Təcili zəng əlçatan deyil"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Səsli xidmət yoxdur"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 078c098..b1ec940b 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID pozivaoca podrazumevano nije ograničen. Sledeći poziv: Nije ograničen."</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Usluga nije dobavljena."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Ne možete da promenite podešavanje ID-a korisnika."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nema usluge mobilnih podataka"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hitni pozivi nisu dostupni"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nema glasovne usluge"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 023e82c..40605c5 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -74,6 +74,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Налады ідэнтыфікатару АВН па змаўчанні: не абмяжавана. Наступны выклік: не абмежавана"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Служба не прадастаўляецца."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Вы не можаце змяніць налады ідэнтыфікатара абанента, якi тэлефануе."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мабільная перадача даных недаступная"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Экстранныя выклікі недаступныя"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Няма сэрвісу галасавых выклікаў"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index aaa080a..a1c9d1c 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Стандартната идентификация на повикванията е „разрешено“. За следващото обаждане тя е разрешена."</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Услугата не е обезпечена."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Не можете да променяте настройката за идентификация на обажданията."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Няма достъп до мобилната услуга за данни"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Няма достъп до спешните обаждания"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Няма услуга за гласови обаждания"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index ee1db8e..fd69acd 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ডিফল্টরূপে কলার আইডি সীমাবদ্ধ করা থাকে না৷ পরবর্তী কল: সীমাবদ্ধ নয়"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"পরিষেবা প্রস্তুত নয়৷"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"আপনি কলার আইডি এর সেটিংস পরিবর্তন করতে পারবেন না৷"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"মোবাইল ডেটা পরিষেবা নেই"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"জরুরি কল করা যাবে না"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ভয়েস পরিষেবা নেই"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 20f6bc1..455c9e9 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -73,6 +73,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Prikaz ID-a pozivaoca u zadanim postavkama nije zabranjen. Sljedeći poziv: nije zabranjen"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Uslugu nije moguće koristiti."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Ne možete promijeniti postavke ID-a pozivaoca."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"Podaci su prebačeni na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"To uvijek možete promijeniti u postavkama"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nema usluge prijenosa podataka na mobilnoj mreži"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hitni pozivi su nedostupni"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nema usluge govornih poziva"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 2142b60..43dc676 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"El valor predeterminat de l\'identificador de trucada és no restringit. Trucada següent: no restringit"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"No s\'ha proveït el servei."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"No pots canviar la configuració de l\'identificador de trucada."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"No hi ha servei de dades mòbils"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Les trucades d\'emergència no estan disponibles"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sense servei de veu"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 7720d08..132a9f5 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -74,6 +74,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Ve výchozím nastavení není funkce ID volajícího omezena. Příští hovor: Neomezeno"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Služba není zřízena."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Nastavení ID volajícího nesmíte měnit."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Není k dispozici žádná mobilní datová služba"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Tísňová volání jsou nedostupná"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Hlasová volání nejsou k dispozici"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index ecd6407..9dd4334 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -72,6 +72,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Standarder for opkalds-id til ikke begrænset. Næste opkald: Ikke begrænset"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Tjenesten provisioneres ikke."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Du kan ikke ændre indstillingen for opkalds-id\'et."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"Der blev skiftet til <xliff:g id="CARRIERDISPLAY">%s</xliff:g>-data"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"Du kan altid ændre dette under Indstillinger"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ingen mobildatatjeneste"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Det er ikke muligt at foretage nødopkald"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ingen taletjeneste"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 3d5985c9..661c2cd 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Anrufer-ID ist standardmäßig nicht beschränkt. Nächster Anruf: Nicht beschränkt"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Dienst nicht eingerichtet."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Du kannst die Einstellung für die Anrufer-ID nicht ändern."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Kein mobiler Datendienst"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Notrufe nicht möglich"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Keine Anrufe"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index f84a9fb..8cb3989b 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -72,6 +72,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Η αναγνώριση κλήσης βρίσκεται από προεπιλογή στην \"μη περιορισμένη\". Επόμενη κλήση: Μη περιορισμένη"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Η υπηρεσία δεν προβλέπεται."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Δεν μπορείτε να αλλάξετε τη ρύθμιση του αναγνωριστικού καλούντος."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"Έγινε εναλλαγή των δεδομένων σε <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"Μπορείτε να αλλάξετε αυτήν την επιλογή ανά πάσα στιγμή στις Ρυθμίσεις"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Δεν υπάρχει υπηρεσία δεδομένων κινητής τηλεφωνίας"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Οι κλήσεις έκτακτης ανάγκης δεν είναι διαθέσιμες"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Δεν υπάρχει φωνητική υπηρεσία"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 4c0510b..75db3826 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -72,6 +72,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Caller ID defaults to not restricted. Next call: Not restricted"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Service not provisioned."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"You can\'t change the caller ID setting."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"Switched data to <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"You can change this at any time in Settings"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"No mobile data service"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Emergency calling unavailable"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"No voice service"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 875ddf9..883cd55 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -72,6 +72,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Caller ID defaults to not restricted. Next call: Not restricted"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Service not provisioned."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"You can\'t change the caller ID setting."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"Switched data to <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"You can change this at any time in Settings"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"No mobile data service"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Emergency calling unavailable"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"No voice service"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 6e034b7..c4c19da 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -72,6 +72,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Caller ID defaults to not restricted. Next call: Not restricted"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Service not provisioned."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"You can\'t change the caller ID setting."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"Switched data to <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"You can change this at any time in Settings"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"No mobile data service"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Emergency calling unavailable"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"No voice service"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 643f27f..d19199d 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -72,6 +72,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Caller ID defaults to not restricted. Next call: Not restricted"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Service not provisioned."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"You can\'t change the caller ID setting."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"Switched data to <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"You can change this at any time in Settings"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"No mobile data service"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Emergency calling unavailable"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"No voice service"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 91e99ff..9037d70 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -72,6 +72,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‎Caller ID defaults to not restricted. Next call: Not restricted‎‏‎‎‏‎"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎Service not provisioned.‎‏‎‎‏‎"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎You can\'t change the caller ID setting.‎‏‎‎‏‎"</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎Switched data to ‎‏‎‎‏‏‎<xliff:g id="CARRIERDISPLAY">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎You can change this anytime in Settings‎‏‎‎‏‎"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎No mobile data service‎‏‎‎‏‎"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎Emergency calling unavailable‎‏‎‎‏‎"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎No voice service‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 6a45205..9441f27 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"El Identificador de llamadas está predeterminado en no restringido. Llamada siguiente: no restringido"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Servicio no suministrado."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"No puedes cambiar la configuración del identificador de llamadas."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"No hay ningún servicio de datos móviles"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Servicio de llamadas de emergencia no disponible"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sin servicio de voz"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 66f67b3..753aa8b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"La identificación del emisor presenta el valor predeterminado de no restringido. Siguiente llamada: No restringido"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"El servicio no se suministra."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"No puedes modificar la identificación de emisor."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"No hay ningún servicio de datos móviles"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Servicio de llamadas de emergencia no disponible"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sin servicio de voz"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 349a6b2..1789793 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Helistaja ID pole vaikimisi piiratud. Järgmine kõne: pole piiratud"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Teenus pole ette valmistatud."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Helistaja ID seadet ei saa muuta."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Mobiilne andmesideteenus puudub"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hädaabikõned pole saadaval"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Häälkõned pole saadaval"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d4759d5..8a7d073 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenik gabe."</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Zerbitzua ez da hornitu."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Ezin duzu aldatu deitzailearen identitatearen ezarpena."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ez dago mugikorreko datu-zerbitzurik"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Ezin da egin larrialdi-deirik"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ez dago ahots-deien zerbitzurik"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 4064353..88bbcc6 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"پیش‌فرض شناسه تماس‌گیرنده روی غیرمحدود است. تماس بعدی: بدون محدودیت"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"سرویس دارای مجوز نیست."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"‏شما می‎توانید تنظیم شناسه تماس‌گیرنده را تغییر دهید."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"بدون سرویس داده تلفن همراه"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"تماس اضطراری دردسترس نیست"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"سرویس صوتی دردسترس نیست"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 8fedfb7..da3a91e 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Soittajan tunnukseksi muutetaan rajoittamaton. Seuraava puhelu: ei rajoitettu"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Palvelua ei tarjota."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Et voi muuttaa soittajan tunnuksen asetusta."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ei mobiilidatapalvelua"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hätäpuhelut eivät ole käytettävissä"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ei äänipuheluja"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index e63b734..d05d97b 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Ce service n\'est pas pris en charge."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Impossible de modifier le paramètre relatif au numéro de l\'appelant."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Aucun service de données cellulaires"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Le service d\'appel d\'urgence n\'est pas accessible"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Aucun service vocal"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 019fdf2..cb2e201 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Ce service n\'est pas pris en charge."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Impossible de modifier le paramètre relatif au numéro de l\'appelant."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Aucun service de données mobiles"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Appels d\'urgence non disponibles"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Aucun service vocal"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 219299f..a3aed41 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: non restrinxido"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Servizo non ofrecido."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Non podes cambiar a configuración do identificador de chamada."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Non hai servizo de datos para móbiles"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"As chamadas de emerxencia non están dispoñibles"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Non hai servizo de chamadas de voz"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 90dda1a..2d9210b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"કૉલર ID પ્રતિબંધિત નહીં પર ડિફોલ્ટ છે. આગલો કૉલ: પ્રતિબંધિત નહીં"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"સેવાની જોગવાઈ કરી નથી."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"તમે કૉલર ID સેટિંગ બદલી શકતાં નથી."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"કોઈ મોબાઇલ ડેટા સેવા નથી"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"કોટોકટીની કૉલિંગ સેવા અનુપલબ્ધ"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"કોઈ વૉઇસ સેવા નથી"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index af5bc1f..9f0e936 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कॉलर आईडी डिफ़ॉल्ट रूप से सीमित नहीं है. अगली कॉल: सीमित नहीं"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवा प्रावधान की हुई नहीं है."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"आप कॉलर आईडी सेटिंग नहीं बदल सकते."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"मोबाइल डेटा सेवा पर रोक लगा दी गई है"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपातकालीन कॉल पर रोक लगा दी गई है"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"कोई वॉइस सेवा नहीं है"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 87df29a..40a49db 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -73,6 +73,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zadana postavka ID-a pozivatelja nema ograničenje. Sljedeći poziv: Nije ograničen"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Usluga nije rezervirana."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Ne možete promijeniti postavku ID-a pozivatelja."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"Podaci su prebačeni na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"To uvijek možete promijeniti u postavkama"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nema podatkovne mobilne usluge"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hitni pozivi nisu dostupni"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nema glasovnih usluga"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3762fde..cf6132f 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"A hívóazonosító alapértelmezett értéke nem korlátozott. Következő hívás: nem korlátozott"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"A szolgáltatás nincs biztosítva."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Nem tudja módosítani a hívó fél azonosítója beállítást."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nincs mobiladat-szolgáltatás"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Segélyhívás nem lehetséges"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Hangszolgáltatás letiltva"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index a11e24b..4da9e3b 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Զանգողի ID-ն լռելյայն չսահմանափակված է: Հաջորդ զանգը` չսահմանափակված"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Ծառայությունը չի տրամադրվում:"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Դուք չեք կարող փոխել զանգողի ID-ի կարգավորումները:"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Բջջային ինտերնետի ծառայությունն արգելափակված է"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Շտապ կանչերը հասանելի չեն"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ձայնային ծառայությունն անհասանելի է"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index dbccee9..2f2e524 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID penelepon diatur default ke tidak dibatasi. Panggilan selanjutnya: Tidak dibatasi"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Layanan tidak diperlengkapi."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Anda tidak dapat mengubah setelan ID penelepon."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Tidak ada layanan data seluler"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Panggilan darurat tidak tersedia"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Tidak ada layanan panggilan suara"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index cfefc03..82a1b66 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Númerabirting er sjálfgefið án takmarkana. Næsta símtal: Án takmarkana"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Þjónustu ekki útdeilt."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Þú getur ekki breytt stillingu númerabirtingar."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Engin gagnaþjónusta fyrir farsíma"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Neyðarsímtöl eru ekki í boði"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Símtöl eru ekki í boði"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b05bf79..b499eea 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID chiamante generalmente non limitato. Prossima chiamata: non limitato"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Servizio non fornito."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Non è possibile modificare l\'impostazione ID chiamante."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nessun servizio dati mobile"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Chiamate di emergenza non disponibili"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nessun servizio di telefonia"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 8656fce..2ef4934 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -74,6 +74,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"זיהוי מתקשר עובר כברירת מחדל למצב לא מוגבל. השיחה הבאה: לא מוגבלת"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"השירות לא הוקצה."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"אינך יכול לשנות את הגדרת זיהוי המתקשר."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"הנתונים עברו אל <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"תמיד אפשר לשנות זאת ב\'הגדרות\'"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"אין שירות של חבילת גלישה"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"שיחות חירום לא זמינות"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"אין אפשרות לבצע שיחות רגילות"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 69d0b9d..53fa057 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"既定: 発信者番号通知、次の発信: 通知"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"提供可能なサービスがありません。"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"発信者番号の設定は変更できません。"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"モバイルデータ サービスのブロック"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"緊急通報のブロック"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"音声通話サービス停止"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 6d32f25..57ff75a 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ნაგულისხმებად დაყენებულია ნომრის დაფარვის გამორთვა. შემდეგი ზარი: არ არის დაფარული."</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"სერვისი არ არის მიწოდებული."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"არ შეგიძლიათ აბონენტის ID პარამეტრების შეცვლა."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"მობილური ინტერნეტის სერვისი არ არის"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"გადაუდებელი ზარი მიუწვდომელია"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ხმოვანი ზარების სერვისი არ არის"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 0d9fd3f..8f5b1c0 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Қоңырау шалушының жеке анықтағышы бастапқы бойынша шектелмеген. Келесі қоңырау: Шектелмеген"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Қызмет ұсынылмаған."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Қоңырау шалушы идентификаторы параметрін өзгерту мүмкін емес."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобильдік интернет қызметі жоқ"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Жедел қызметке қоңырау шалу қолжетімді емес"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Дауыстық қоңыраулар қызметі жоқ"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 0c82b66..1eae9db 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"មិន​បាន​ដាក់កម្រិត​លំនាំដើម​លេខ​សម្គាល់​អ្នក​ហៅ។ ការ​ហៅ​បន្ទាប់៖ មិន​បាន​ដាក់​កម្រិត។"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"មិន​បាន​ផ្ដល់​សេវាកម្ម។"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"អ្នក​មិន​អាច​ប្ដូរ​ការ​កំណត់​លេខ​សម្គាល់​អ្នក​ហៅ​បានទេ។"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"គ្មាន​​សេវាកម្ម​ទិន្នន័យ​ចល័ត​ទេ"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ការ​ហៅ​បន្ទាន់​មិន​អាច​ប្រើ​បាន​ទេ"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"គ្មាន​សេវាកម្ម​ជា​សំឡេង​ទេ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index e27527f..821138a 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ಕರೆಮಾಡುವವರ ID ಅನ್ನು ನಿರ್ಬಂಧಿಸದಿರುವಂತೆ ಡಿಫಾಲ್ಟ್ ಮಾಡಲಾಗಿದೆ. ಮುಂದಿನ ಕರೆ: ನಿರ್ಬಂಧಿಸಲಾಗಿಲ್ಲ"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"ಸೇವೆಯನ್ನು ಪೂರೈಸಲಾಗಿಲ್ಲ."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"ನೀವು ಕಾಲರ್‌ ID ಸೆಟ್ಟಿಂಗ್‌ ಬದಲಾಯಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ಮೊಬೈಲ್ ಡೇಟಾ ಸೇವೆಯಿಲ್ಲ"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ತುರ್ತು ಕರೆ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ಧ್ವನಿ ಸೇವೆಯಿಲ್ಲ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index c953a39..5f3c7ca 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"발신자 번호가 기본적으로 제한되지 않음으로 설정됩니다. 다음 통화: 제한되지 않음"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"서비스가 준비되지 않았습니다."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"발신자 번호 설정을 변경할 수 없습니다."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"모바일 데이터 서비스가 차단됨"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"긴급 전화를 사용할 수 없음"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"음성 서비스를 이용할 수 없음"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index dccc4a6..6e7b355 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Номурду аныктоонун демейки абалы \"чектелбейт\" деп коюлган. Кийинки чалуу: Чектелбейт"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Кызмат камсыздалган эмес."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Чалуучунун далдаштырма дайындары жөндөөлөрүн өзгөртө албайсыз."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобилдик Интернет кызматы жок"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Шашылыш чалуу бөгөттөлгөн"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Аудио чалуу кызматы бөгөттөлгөн"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index c6524de..7a76c2c 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ໝາຍເລກຜູ່ໂທ ໄດ້ຮັບການຕັ້ງຄ່າເລີ່ມຕົ້ນເປັນ ບໍ່ຖືກຈຳກັດ. ການໂທຄັ້ງຕໍ່ໄປ: ບໍ່ຖືກຈຳກັດ."</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"ບໍ່ໄດ້ເປີດໃຊ້ບໍລິການ."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"ທ່ານບໍ່ສາມາດປ່ຽນແປງການຕັ້ງຄ່າ Caller ID"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ບໍ່ມີບໍລິການອິນເຕີເນັດມືຖື"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ບໍ່ສາມາດໃຊ້ການໂທສຸກເສີນໄດ້"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ບໍ່ມີບໍລິການໂທສຽງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index adf30e8..e0d1fc7 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -74,6 +74,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Skambintojo ID pagal numatytuosius nustatymus yra neapribotas. Kitas skambutis: neapribotas"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Paslauga neteikiama."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Negalima pakeisti skambinančiojo ID nustatymo."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Duomenų paslaugos mobiliesiems nėra"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Skambučių pagalbos numeriu paslaugos nėra"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Balso skambučių paslauga neteikiama"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 5631521..ec8cb7d 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zvanītāja ID noklusējumi ir iestatīti uz Nav ierobežots. Nākamais zvans: nav ierobežots"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Pakalpojums netiek nodrošināts."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Zvanītāja ID iestatījumu nevar mainīt."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nav pieejams neviens datu pakalpojums mobilajām ierīcēm"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Nav pieejami ārkārtas izsaukumi"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Balss izsaukumu pakalpojums nedarbojas"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index a45d0a7..442047d 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Стандардно, ID на повикувач не е скриен. Следен повик: не е скриен"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Услугата не е предвидена."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Не може да го промените поставувањето за ID на повикувач."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Нема услуга за мобилен интернет"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Итните повици се недостапни"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Нема услуга за говорни повици"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 492cd54..5b59e66 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -72,6 +72,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"നിയന്ത്രിക്കേണ്ടതല്ലാത്ത സ്ഥിര കോളർ ഐഡികൾ. അടുത്ത കോൾ: നിയന്ത്രിച്ചിട്ടില്ല"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"സേവനം വ്യവസ്ഥ ചെയ്‌തിട്ടില്ല."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"വിളിച്ച നമ്പർ ക്രമീകരണം നിങ്ങൾക്ക് മാറ്റാനാവില്ല."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> എന്നതിലേക്ക് ഡാറ്റ മാറ്റി"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"നിങ്ങൾക്ക് ക്രമീകരണത്തിൽ ഏതുസമയത്തും ഇത് മാറ്റാം"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"മൊബൈൽ ഡാറ്റാ സേവനമില്ല"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"എമർജൻസി കോളിംഗ് ലഭ്യമല്ല"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"വോയ്സ് സേവനമില്ല"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 2c8aaae..03deebb 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Дуудлага хийгчийн ID хязгаарлагдсан. Дараагийн дуудлага: Хязгаарлагдсан"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Үйлчилгээ провишн хийгдээгүй ."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Та дуудлага хийгчийн ID тохиргоог солиж чадахгүй."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобайл дата үйлчилгээ алга"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Яаралтай дуудлага боломжтой"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Дуу хоолойны үйлчилгээ алга"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index d47fea35..1d25e67 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कॉलर आयडी डीफॉल्‍ट रूपात प्रतिबंधित नाही वर सेट असतो. पुढील कॉल: प्रतिबंधित नाही"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवेची तरतूद केलेली नाही."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"तुम्ही कॉलर आयडी सेटिंग बदलू शकत नाही."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"मोबाइल डेटा सेवा नाही"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आणीबाणी कॉलिंग अनुपलब्‍ध आहे"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"व्हॉइस सेवा नाही"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index deb343d..72d3f2f 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID pemanggil secara lalainya ditetapkan kepada tidak dihadkan. Panggilan seterusnya: Tidak terhad"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Perkhidmatan yang tidak diuntukkan."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Anda tidak boleh mengubah tetapan ID pemanggil."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Tiada perkhidmatan data mudah alih"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Panggilan kecemasan tidak tersedia"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Tiada perkhidmatan suara"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 5f41672..5ff033d 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ပုံသေအားဖြင့် ခေါ်ဆိုသူအိုင်ဒီ(Caller ID)အား ကန့်သတ်မထားပါ။ နောက်ထပ်အဝင်ခေါ်ဆိုမှု-ကန့်သတ်မထားပါ။"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"ဝန်ဆောင်မှုအား ကန့်သတ်မထားပါ"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"သင်သည် ခေါ်ဆိုသူ ID ဆက်တင်ကို မပြောင်းလဲနိုင်ပါ။"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"မိုဘိုင်း ဒေတာဝန်ဆောင်မှု မရှိပါ"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"အရေးပေါ်ခေါ်ဆိုမှု မရနိုင်ပါ"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ဖုန်းဝန်ဆောင်မှု မရှိပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 0c9a983..bb4fbe4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Nummervisning er ikke begrenset som standard. Neste anrop: Ikke begrenset"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"SIM-kortet er ikke tilrettelagt for tjenesten."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Du kan ikke endre innstillingen for anrops-ID."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ingen mobildatatjeneste"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Nødanrop er utilgjengelig"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ingen taletjeneste"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index e95d48b..ea96df0 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कलर ID पूर्वनिर्धारितको लागि रोकावट छैन। अर्को कल: रोकावट छैन"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवाको व्यवस्था छैन।"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"तपाईं कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"कुनै पनि मोबाइल डेटा सेवा उपलब्ध छैन"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपत्‌कालीन कल सेवा उपलब्ध छैन"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"कुनै पनि भ्वाइस सेवा उपलब्ध छैन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5723510..24877dd 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Beller-ID standaard ingesteld op \'onbeperkt\'. Volgend gesprek: onbeperkt."</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Service niet voorzien."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"U kunt de instelling voor de beller-ID niet wijzigen."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Geen service voor mobiele data"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Noodoproepen niet beschikbaar"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Geen belservice"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index c4150dc..456fc83 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ ନୁହେଁ"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"ସେବାର ସୁବିଧା ନାହିଁ।"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"ଆପଣ କଲର୍‍ ID ସେଟିଙ୍ଗ ବଦଳାଇପାରିବେ ନାହିଁ।"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"କୌଣସି ମୋବାଇଲ୍ ଡାଟା ସେବା ନାହିଁ"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ଜରୁରୀକାଳୀନ କଲ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"କୌଣସି ଭଏସ୍‍ ସେବା ନାହିଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 96917c0..79ab897 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ਪ੍ਰਤਿਬੰਧਿਤ ਨਾ ਕਰਨ ਲਈ ਕਾਲਰ ਆਈ.ਡੀ. ਪੂਰਵ-ਨਿਰਧਾਰਤ। ਅਗਲੀ ਕਾਲ: ਪ੍ਰਤਿਬੰਧਿਤ ਨਹੀਂ"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"ਸੇਵਾ ਪ੍ਰਬੰਧਿਤ ਨਹੀਂ ਹੈ।"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"ਤੁਸੀਂ ਕਾਲਰ ਆਈ.ਡੀ. ਸੈਟਿੰਗ ਨਹੀਂ ਬਦਲ ਸਕਦੇ।"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ਕੋਈ ਮੋਬਾਈਲ ਡਾਟਾ ਸੇਵਾ ਨਹੀਂ"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ਸੰਕਟਕਾਲੀਨ ਕਾਲਿੰਗ ਉਪਲਬਧ ਨਹੀਂ"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ਕੋਈ ਆਵਾਜ਼ੀ ਸੇਵਾ ਨਹੀਂ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index be9d322..5e3a64a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -74,6 +74,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID rozmówcy ustawiony jest domyślnie na „nie zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Usługa nie jest świadczona."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Nie możesz zmienić ustawienia ID rozmówcy."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Brak komórkowej usługi transmisji danych"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Połączenia alarmowe są niedostępne"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Brak usługi połączeń głosowych"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index ff352b1..34c0f0c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Não restrita"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"O serviço não foi habilitado."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Não é possível alterar a configuração do identificador de chamadas."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nenhum serviço móvel de dados"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Chamadas de emergência indisponíveis"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sem serviço de voz"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index d343af6..0fb835b 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -73,6 +73,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID do autor da chamada é predefinido com não restrito. Chamada seguinte: Não restrita"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Serviço não fornecido."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Não pode alterar a definição da identificação de chamadas."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"Os dados móveis foram alterados para o operador <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"Pode alterar isto em qualquer altura nas Definições"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Sem serviço de dados móveis"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Chamadas de emergência indisponíveis"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sem serviço de voz"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index ff352b1..34c0f0c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Não restrita"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"O serviço não foi habilitado."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Não é possível alterar a configuração do identificador de chamadas."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nenhum serviço móvel de dados"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Chamadas de emergência indisponíveis"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sem serviço de voz"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index b560b07..88aab00 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: nerestricționat"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Nu se asigură accesul la acest serviciu."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Nu poți modifica setarea pentru ID-ul apelantului."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Fără serviciu de date mobile"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Apelurile de urgență nu sunt disponibile"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Fără servicii vocale"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index fbe67e2..33bfe77 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -74,6 +74,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Идентификация абонента по умолчанию не запрещена. След. вызов: разрешена"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Услуга не предоставляется."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Невозможно изменить параметр идентификатора вызывающего абонента."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобильный Интернет недоступен"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Экстренные вызовы недоступны"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Голосовые вызовы недоступны"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 4cec877..dd5b817 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"අමතන්නාගේ ID සුපුරුදු අනුව සීමා වී නැත. මීළඟ ඇමතුම: සීමා කර ඇත"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"සේවාවන් සපයා නැත."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"අමතන්නාගේ ID සැකසීම ඔබට වෙනස්කල නොහැක."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ජංගම දත්ත සේවාව නැත"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"හදිසි ඇමතුම් ලබා ගත නොහැකිය"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"හඬ සේවාව නැත"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index b98364a..40abf79 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -74,6 +74,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"V predvolenom nastavení nie je identifikácia volajúceho obmedzená. Ďalší hovor: Bez obmedzenia"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Služba nie je poskytovaná."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Nemôžete meniť nastavenie identifikácie volajúcich."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Žiadna mobilná dátová služba"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Tiesňové volania nie sú k dispozícii"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Žiadne hlasové hovory"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 6972abb..53a0321 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -74,6 +74,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID klicatelja je ponastavljen na neomejeno. Naslednji klic: ni omejeno"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Storitev ni nastavljena in omogočena."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Ne morete spremeniti nastavitve ID-ja klicatelja."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ni mobilne podatkovne storitve"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Klicanje v sili ni na voljo"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ni storitve za glasovne klice"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index cbac0f0..6c3f145 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID-ja e telefonuesit kalon me paracaktim në listën e të telefonuesve të pakufizuar. Telefonata e radhës: e pakufizuar!"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Shërbimi nuk është përgatitur."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Nuk mund ta ndryshosh cilësimin e ID-së së telefonuesit."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nuk ka shërbim të të dhënave celulare"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Telefonatat e urgjencës nuk ofrohen"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nuk ka shërbim zanor"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index d5549e7..d23b056 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -73,6 +73,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ИД позиваоца подразумевано није ограничен. Следећи позив: Није ограничен."</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Услуга није добављена."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Не можете да промените подешавање ИД-а корисника."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Нема услуге мобилних података"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Хитни позиви нису доступни"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Нема гласовне услуге"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index d1c579d..ee6a8ac 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Nummerpresentatörens standardinställning är inte blockerad. Nästa samtal: Inte blockerad"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Tjänsten är inte etablerad."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Det går inte att ändra inställningen för nummerpresentatör."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ingen mobildatatjänst"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Det går inte att ringa nödsamtal"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Tjänsten för röstsamtal har blockerats"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 2bc3f57..7b9d89f 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Haijazuiliwa"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Huduma haitathminiwi."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Hauwezi kubadilisha mpangilio wa kitambulisho cha anayepiga."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Hakuna huduma ya data kwa vifaa vya mkononi"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Huduma ya kupiga simu za dharura haipatikani"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Hakuna huduma za simu za sauti"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 9e48a47..2a0fc2c 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"அழைப்பாளர் ஐடி ஆனது வரையறுக்கப்படவில்லை என்பதற்கு இயல்பாக அமைக்கப்பட்டது. அடுத்த அழைப்பு: வரையறுக்கப்படவில்லை"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"சேவை ஒதுக்கப்படவில்லை."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"அழைப்பாளர் ஐடி அமைப்பை மாற்ற முடியாது."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"மொபைல் டேட்டா சேவையைப் பயன்படுத்த முடியாது"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"அவசர அழைப்பைச் செய்ய முடியாது"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"குரல் சேவை இல்லை"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index eff08bc..a7c2d4e 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"కాలర్ ID ఆటోమేటిక్‌లపై పరిమితి లేదు. తర్వాత కాల్: పరిమితి లేదు"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"సేవ కేటాయించబడలేదు."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"మీరు కాలర్ ID సెట్టింగ్‌ను మార్చలేరు."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"మొబైల్ డేటా సేవ లేదు"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"అత్యవసర కాలింగ్ అందుబాటులో లేదు"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"వాయిస్ సర్వీస్ లేదు"</string>
@@ -2071,7 +2075,7 @@
     <string name="notification_appops_camera_active" msgid="8177643089272352083">"కెమెరా"</string>
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"మైక్రోఫోన్"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"మీ స్క్రీన్‌పై ఇతర యాప్‌ల ద్వారా ప్రదర్శించబడుతోంది"</string>
-    <string name="notification_feedback_indicator" msgid="663476517711323016">"ఫీడ్‌బ్యాక్‌ను అందించండి"</string>
+    <string name="notification_feedback_indicator" msgid="663476517711323016">"ఫీడ్‌బ్యాక్ ఇవ్వండి"</string>
     <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"ఈ నోటిఫికేషన్, ఆటోమేటిక్ సెట్టింగ్‌కు ప్రమోట్ చేయబడింది. ఫీడ్‌బ్యాక్‌ను అందించడానికి ట్యాప్ చేయండి."</string>
     <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ఈ నోటిఫికేషన్ స్థాయి నిశ్శబ్దంగా ఉండేలా తగ్గించబడింది. ఫీడ్‌బ్యాక్‌ను అందించడానికి ట్యాప్ చేయండి."</string>
     <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ఈ నోటిఫికేషన్‌కు ఎక్కువ ర్యాంక్ ఇవ్వబడింది. ఫీడ్‌బ్యాక్‌ను అందించడానికి ట్యాప్ చేయండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 72cbc36..c0ab275 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -72,6 +72,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"หมายเลขผู้โทรได้รับการตั้งค่าเริ่มต้นเป็นไม่จำกัด การโทรครั้งต่อไป: ไม่จำกัด"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"ไม่มีการนำเสนอบริการ"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"คุณไม่สามารถเปลี่ยนการตั้งค่าหมายเลขผู้โทร"</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"เปลี่ยนไปใช้อินเทอร์เน็ตมือถือของ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> แล้ว"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"คุณเปลี่ยนตัวเลือกนี้ได้ทุกเมื่อในการตั้งค่า"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"ไม่มีบริการเน็ตมือถือ"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"หมายเลขฉุกเฉินไม่พร้อมใช้งาน"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"ไม่มีบริการเสียง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index e66999d..7b9807a 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -72,6 +72,8 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Naka-default na hindi pinaghihigpitan ang Caller ID. Susunod na tawag: Hindi pinaghihigpitan"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Hindi naprobisyon ang serbisyo."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Hindi mo mababago ang setting ng caller ID."</string>
+    <string name="auto_data_switch_title" msgid="3286350716870518297">"Nailipat sa <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ang data"</string>
+    <string name="auto_data_switch_content" msgid="803557715007110959">"Puwede mo itong baguhin anumang oras sa Mga Setting"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Walang serbisyo ng data sa mobile"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hindi available ang pang-emergency na pagtawag"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Walang serbisyo para sa boses"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index b6c4b4a..3459cb0 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Arayan kimliği varsayılanları kısıtlanmamıştır. Sonraki çağrı: Kısıtlanmamış"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Hizmet sağlanamadı."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Arayanın kimliği ayarını değiştiremezsiniz."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Mobil veri hizmeti yok"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Acil durum çağrısı kullanılamaz"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Sesli çağrı hizmeti yok"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index c408c51..2483a30 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -74,6 +74,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Ідентиф. абонента за умовч. не обмеж. Наст. дзвінок: не обмежений"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Службу не ініціалізовано."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Ви не можете змінювати налаштування ідентифікатора абонента."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Службу передавання мобільних даних заблоковано"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Екстрені виклики недоступні"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Немає голосової служби"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 7784431..7de793c 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"‏کالر ID کی ڈیفالٹ ترتیب غیر محدود کردہ ہے۔ اگلی کال: غیر محدود کردہ"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"سروس فراہم نہیں کی گئی۔"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"‏آپ کالر ID کی ترتیبات تبدیل نہیں کر سکتے ہیں۔"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"کوئی موبائل ڈیٹا سروس دستیاب نہیں ہے"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"ہنگامی کالنگ دستیاب نہیں ہے"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"کوئی صوتی سروس نہیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 5ac689d1..8c88585 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Qo‘ng‘iroq qiluvchi ma’lumotlari cheklanmagan. Keyingi qo‘ng‘iroq: cheklanmagan"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Xizmat ishalamaydi."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Qo‘ng‘iroq qiluvchining ID raqami sozlamasini o‘zgartirib bo‘lmaydi."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Mobil internet ishlamayapti"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Favqulodda chaqiruv ishlamayapti"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ovozli chaqiruvlar ishlamaydi"</string>
@@ -1592,7 +1596,7 @@
     <string name="issued_by" msgid="7872459822431585684">"Tegishli:"</string>
     <string name="validity_period" msgid="1717724283033175968">"Yaroqliligi:"</string>
     <string name="issued_on" msgid="5855489688152497307">"Chiqarilgan sanasi:"</string>
-    <string name="expires_on" msgid="1623640879705103121">"Amal qilish muddati:"</string>
+    <string name="expires_on" msgid="1623640879705103121">"Muddati:"</string>
     <string name="serial_number" msgid="3479576915806623429">"Serial raqam:"</string>
     <string name="fingerprints" msgid="148690767172613723">"Barmoq izlari:"</string>
     <string name="sha256_fingerprint" msgid="7103976380961964600">"SHA-256 barmoq izi:"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e1b479c..68bb062 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Số gọi đến mặc định thành không bị giới hạn. Cuộc gọi tiếp theo. Không bị giới hạn"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Dịch vụ không được cấp phép."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Bạn không thể thay đổi cài đặt ID người gọi."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Không có dịch vụ dữ liệu di động"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Không có dịch vụ gọi khẩn cấp"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Không có dịch vụ thoại"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 5fce25e..f2b033e 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"默认显示本机号码,在下一次通话中也显示"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"未提供服务。"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"您无法更改来电显示设置。"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"无法使用移动数据服务"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"无法使用紧急呼救服务"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"无法使用语音通话服务"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 61a3f20..63995bb 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"預設顯示來電號碼,下一通電話也繼續顯示。"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"未提供此服務。"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"您無法更改來電顯示設定。"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"無法使用流動數據服務"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"無法撥打緊急電話"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"沒有語音服務"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 10dc699..55f9321 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"預設顯示本機號碼,下一通電話也繼續顯示。"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"無法提供此服務。"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"你無法變更來電顯示設定。"</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"沒有行動數據傳輸服務"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"無法撥打緊急電話"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"無法使用語音通話服務"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 66d639e..2218b76 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -72,6 +72,10 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"I-ID Yomshayeli ishintshela kokungavinjelwe. Ucingo olulandelayo: Aluvinjelwe"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Isevisi ayilungiselelwe."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Ngeke ukwazi ukuguqul izilungiselelo zemininingwane yoshayayo."</string>
+    <!-- no translation found for auto_data_switch_title (3286350716870518297) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_content (803557715007110959) -->
+    <skip />
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ayikho isevisi yedatha yeselula"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Ukushaya okuphuthumayo akutholakali"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ayikho isevisi yezwi"</string>
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java
new file mode 100644
index 0000000..fe3ab62
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java
@@ -0,0 +1,433 @@
+/*
+ * 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 android.hardware.radio.tests.unittests;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.hardware.radio.IRadioService;
+import android.hardware.radio.ITuner;
+import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioMetadata;
+import android.hardware.radio.RadioTuner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class TunerAdapterTest {
+
+    private static final int CALLBACK_TIMEOUT_MS = 30_000;
+    private static final int AM_LOWER_LIMIT_KHZ = 150;
+
+    private static final RadioManager.BandConfig TEST_BAND_CONFIG = createBandConfig();
+
+    private static final ProgramSelector.Identifier FM_IDENTIFIER =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY,
+                    /* value= */ 94300);
+    private static final ProgramSelector FM_SELECTOR =
+            new ProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, FM_IDENTIFIER,
+                    /* secondaryIds= */ null, /* vendorIds= */ null);
+    private static final RadioManager.ProgramInfo FM_PROGRAM_INFO = createFmProgramInfo();
+
+    private RadioTuner mRadioTuner;
+    private ITunerCallback mTunerCallback;
+
+    @Mock
+    private IRadioService mRadioServiceMock;
+    @Mock
+    private Context mContextMock;
+    @Mock
+    private ITuner mTunerMock;
+    @Mock
+    private RadioTuner.Callback mCallbackMock;
+
+    @Before
+    public void setUp() throws Exception {
+        RadioManager radioManager = new RadioManager(mContextMock, mRadioServiceMock);
+
+        doAnswer(invocation -> {
+            mTunerCallback = (ITunerCallback) invocation.getArguments()[3];
+            return mTunerMock;
+        }).when(mRadioServiceMock).openTuner(anyInt(), any(), anyBoolean(), any());
+
+        doAnswer(invocation -> {
+            ProgramSelector program = (ProgramSelector) invocation.getArguments()[0];
+            if (program.getPrimaryId().getType()
+                    != ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY) {
+                throw new IllegalArgumentException();
+            }
+            if (program.getPrimaryId().getValue() < AM_LOWER_LIMIT_KHZ) {
+                mTunerCallback.onTuneFailed(RadioManager.STATUS_BAD_VALUE, program);
+            } else {
+                mTunerCallback.onCurrentProgramInfoChanged(FM_PROGRAM_INFO);
+            }
+            return RadioManager.STATUS_OK;
+        }).when(mTunerMock).tune(any());
+
+        mRadioTuner = radioManager.openTuner(/* moduleId= */ 0, TEST_BAND_CONFIG,
+                /* withAudio= */ true, mCallbackMock, /* handler= */ null);
+    }
+
+    @After
+    public void cleanUp() throws Exception {
+        mRadioTuner.close();
+    }
+
+    @Test
+    public void close_forTunerAdapter() throws Exception {
+        mRadioTuner.close();
+
+        verify(mTunerMock).close();
+    }
+
+    @Test
+    public void setConfiguration_forTunerAdapter() throws Exception {
+        int status = mRadioTuner.setConfiguration(TEST_BAND_CONFIG);
+
+        verify(mTunerMock).setConfiguration(TEST_BAND_CONFIG);
+        assertWithMessage("Status for setting configuration")
+                .that(status).isEqualTo(RadioManager.STATUS_OK);
+    }
+
+    @Test
+    public void getConfiguration_forTunerAdapter() throws Exception {
+        when(mTunerMock.getConfiguration()).thenReturn(TEST_BAND_CONFIG);
+        RadioManager.BandConfig[] bandConfigs = new RadioManager.BandConfig[1];
+
+        int status = mRadioTuner.getConfiguration(bandConfigs);
+
+        assertWithMessage("Status for getting configuration")
+                .that(status).isEqualTo(RadioManager.STATUS_OK);
+        assertWithMessage("Configuration obtained from radio tuner")
+                .that(bandConfigs[0]).isEqualTo(TEST_BAND_CONFIG);
+    }
+
+    @Test
+    public void setMute_forTunerAdapter() {
+        int status = mRadioTuner.setMute(/* mute= */ true);
+
+        assertWithMessage("Status for setting mute")
+                .that(status).isEqualTo(RadioManager.STATUS_OK);
+    }
+
+    @Test
+    public void getMute_forTunerAdapter() throws Exception {
+        when(mTunerMock.isMuted()).thenReturn(true);
+
+        boolean muteStatus = mRadioTuner.getMute();
+
+        assertWithMessage("Mute status").that(muteStatus).isTrue();
+    }
+
+    @Test
+    public void step_forTunerAdapter_succeeds() throws Exception {
+        doAnswer(invocation -> {
+            mTunerCallback.onCurrentProgramInfoChanged(FM_PROGRAM_INFO);
+            return RadioManager.STATUS_OK;
+        }).when(mTunerMock).step(anyBoolean(), anyBoolean());
+
+        int scanStatus = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, /* skipSubChannel= */ false);
+
+        verify(mTunerMock).step(/* skipSubChannel= */ true, /* skipSubChannel= */ false);
+        assertWithMessage("Status for stepping")
+                .that(scanStatus).isEqualTo(RadioManager.STATUS_OK);
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO);
+    }
+
+    @Test
+    public void seek_forTunerAdapter_succeeds() throws Exception {
+        doAnswer(invocation -> {
+            mTunerCallback.onCurrentProgramInfoChanged(FM_PROGRAM_INFO);
+            return RadioManager.STATUS_OK;
+        }).when(mTunerMock).scan(anyBoolean(), anyBoolean());
+
+        int scanStatus = mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, /* skipSubChannel= */ false);
+
+        verify(mTunerMock).scan(/* directionDown= */ true, /* skipSubChannel= */ false);
+        assertWithMessage("Status for seeking")
+                .that(scanStatus).isEqualTo(RadioManager.STATUS_OK);
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO);
+    }
+
+    @Test
+    public void seek_forTunerAdapter_invokesOnErrorWhenTimeout() throws Exception {
+        doAnswer(invocation -> {
+            mTunerCallback.onError(RadioTuner.ERROR_SCAN_TIMEOUT);
+            return RadioManager.STATUS_OK;
+        }).when(mTunerMock).scan(anyBoolean(), anyBoolean());
+
+        mRadioTuner.scan(RadioTuner.DIRECTION_UP, /* skipSubChannel*/ true);
+
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onError(RadioTuner.ERROR_SCAN_TIMEOUT);
+    }
+
+    @Test
+    public void tune_withChannelsForTunerAdapter_succeeds() {
+        int status = mRadioTuner.tune(/* channel= */ 92300, /* subChannel= */ 0);
+
+        assertWithMessage("Status for tuning with channel and sub-channel")
+                .that(status).isEqualTo(RadioManager.STATUS_OK);
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO);
+    }
+
+    @Test
+    public void tune_withValidSelectorForTunerAdapter_succeeds() throws Exception {
+        mRadioTuner.tune(FM_SELECTOR);
+
+        verify(mTunerMock).tune(FM_SELECTOR);
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO);
+    }
+
+
+    @Test
+    public void tune_withInvalidSelectorForTunerAdapter_invokesOnTuneFailed() {
+        ProgramSelector invalidSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_FM,
+                        new ProgramSelector.Identifier(
+                                ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, /* value= */ 100),
+                /* secondaryIds= */ null, /* vendorIds= */ null);
+
+        mRadioTuner.tune(invalidSelector);
+
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS))
+                .onTuneFailed(RadioManager.STATUS_BAD_VALUE, invalidSelector);
+    }
+
+    @Test
+    public void cancel_forTunerAdapter() throws Exception {
+        mRadioTuner.tune(FM_SELECTOR);
+
+        mRadioTuner.cancel();
+
+        verify(mTunerMock).cancel();
+    }
+
+    @Test
+    public void cancelAnnouncement_forTunerAdapter() throws Exception {
+        mRadioTuner.cancelAnnouncement();
+
+        verify(mTunerMock).cancelAnnouncement();
+    }
+
+    @Test
+    public void getProgramInfo_beforeProgramInfoSetForTunerAdapter() {
+        RadioManager.ProgramInfo[] programInfoArray = new RadioManager.ProgramInfo[1];
+
+        int status = mRadioTuner.getProgramInformation(programInfoArray);
+
+        assertWithMessage("Status for getting null program info")
+                .that(status).isEqualTo(RadioManager.STATUS_INVALID_OPERATION);
+    }
+
+    @Test
+    public void getProgramInfo_afterTuneForTunerAdapter() {
+        mRadioTuner.tune(FM_SELECTOR);
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO);
+        RadioManager.ProgramInfo[] programInfoArray = new RadioManager.ProgramInfo[1];
+
+        int status = mRadioTuner.getProgramInformation(programInfoArray);
+
+        assertWithMessage("Status for getting program info")
+                .that(status).isEqualTo(RadioManager.STATUS_OK);
+        assertWithMessage("Program info obtained from radio tuner")
+                .that(programInfoArray[0]).isEqualTo(FM_PROGRAM_INFO);
+    }
+
+    @Test
+    public void getMetadataImage_forTunerAdapter() throws Exception {
+        Bitmap bitmapExpected = Mockito.mock(Bitmap.class);
+        when(mTunerMock.getImage(anyInt())).thenReturn(bitmapExpected);
+        int imageId = 1;
+
+        Bitmap image = mRadioTuner.getMetadataImage(/* id= */ imageId);
+
+        assertWithMessage("Image obtained from id %s", imageId)
+                .that(image).isEqualTo(bitmapExpected);
+    }
+
+    @Test
+    public void isAnalogForced_forTunerAdapter() throws Exception {
+        when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG)).thenReturn(true);
+
+        boolean isAnalogForced = mRadioTuner.isAnalogForced();
+
+        assertWithMessage("Forced analog playback switch")
+                .that(isAnalogForced).isTrue();
+    }
+
+    @Test
+    public void setAnalogForced_forTunerAdapter() throws Exception {
+        boolean analogForced = true;
+
+        mRadioTuner.setAnalogForced(analogForced);
+
+        verify(mTunerMock).setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, analogForced);
+    }
+
+    @Test
+    public void isConfigFlagSupported_forTunerAdapter() throws Exception {
+        when(mTunerMock.isConfigFlagSupported(RadioManager.CONFIG_DAB_DAB_LINKING))
+                .thenReturn(true);
+
+        boolean dabFmSoftLinking =
+                mRadioTuner.isConfigFlagSupported(RadioManager.CONFIG_DAB_DAB_LINKING);
+
+        assertWithMessage("Support for DAB-DAB linking config flag")
+                .that(dabFmSoftLinking).isTrue();
+    }
+
+    @Test
+    public void isConfigFlagSet_forTunerAdapter() throws Exception {
+        when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_DAB_FM_SOFT_LINKING))
+                .thenReturn(true);
+
+        boolean dabFmSoftLinking =
+                mRadioTuner.isConfigFlagSet(RadioManager.CONFIG_DAB_FM_SOFT_LINKING);
+
+        assertWithMessage("DAB-FM soft linking config flag")
+                .that(dabFmSoftLinking).isTrue();
+    }
+
+    @Test
+    public void setConfigFlag_forTunerAdapter() throws Exception {
+        boolean dabFmLinking = true;
+
+        mRadioTuner.setConfigFlag(RadioManager.CONFIG_DAB_FM_LINKING, dabFmLinking);
+
+        verify(mTunerMock).setConfigFlag(RadioManager.CONFIG_DAB_FM_LINKING, dabFmLinking);
+    }
+
+    @Test
+    public void getParameters_forTunerAdapter() throws Exception {
+        List<String> parameterKeys = Arrays.asList("ParameterKeyMock");
+        Map<String, String> parameters = Map.of("ParameterKeyMock", "ParameterValueMock");
+        when(mTunerMock.getParameters(parameterKeys)).thenReturn(parameters);
+
+        assertWithMessage("Parameters obtained from radio tuner")
+                .that(mRadioTuner.getParameters(parameterKeys)).isEqualTo(parameters);
+    }
+
+    @Test
+    public void setParameters_forTunerAdapter() throws Exception {
+        Map<String, String> parameters = Map.of("ParameterKeyMock", "ParameterValueMock");
+        when(mTunerMock.setParameters(parameters)).thenReturn(parameters);
+
+        assertWithMessage("Parameters set for radio tuner")
+                .that(mRadioTuner.setParameters(parameters)).isEqualTo(parameters);
+    }
+
+    @Test
+    public void isAntennaConnected_forTunerAdapter() throws Exception {
+        mTunerCallback.onAntennaState(/* connected= */ false);
+
+        assertWithMessage("Antenna connection status")
+                .that(mRadioTuner.isAntennaConnected()).isFalse();
+    }
+
+    @Test
+    public void hasControl_forTunerAdapter() throws Exception {
+        when(mTunerMock.isClosed()).thenReturn(true);
+
+        assertWithMessage("Control on tuner").that(mRadioTuner.hasControl()).isFalse();
+    }
+
+    @Test
+    public void onConfigurationChanged_forTunerCallbackAdapter() throws Exception {
+        mTunerCallback.onConfigurationChanged(TEST_BAND_CONFIG);
+
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS))
+                .onConfigurationChanged(TEST_BAND_CONFIG);
+    }
+
+    @Test
+    public void onTrafficAnnouncement_forTunerCallbackAdapter() throws Exception {
+        mTunerCallback.onTrafficAnnouncement(/* active= */ true);
+
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS))
+                .onTrafficAnnouncement(/* active= */ true);
+    }
+
+    @Test
+    public void onEmergencyAnnouncement_forTunerCallbackAdapter() throws Exception {
+        mTunerCallback.onEmergencyAnnouncement(/* active= */ true);
+
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS))
+                .onEmergencyAnnouncement(/* active= */ true);
+    }
+
+    @Test
+    public void onBackgroundScanAvailabilityChange_forTunerCallbackAdapter() throws Exception {
+        mTunerCallback.onBackgroundScanAvailabilityChange(/* isAvailable= */ false);
+
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS))
+                .onBackgroundScanAvailabilityChange(/* isAvailable= */ false);
+    }
+
+    @Test
+    public void onProgramListChanged_forTunerCallbackAdapter() throws Exception {
+        mTunerCallback.onProgramListChanged();
+
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramListChanged();
+    }
+
+    @Test
+    public void onParametersUpdated_forTunerCallbackAdapter() throws Exception {
+        Map<String, String> parametersExpected = Map.of("ParameterKeyMock", "ParameterValueMock");
+
+        mTunerCallback.onParametersUpdated(parametersExpected);
+
+        verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onParametersUpdated(parametersExpected);
+    }
+
+    private static RadioManager.ProgramInfo createFmProgramInfo() {
+        return new RadioManager.ProgramInfo(FM_SELECTOR, FM_IDENTIFIER, FM_IDENTIFIER,
+                /* relatedContent= */ null, /* infoFlags= */ 0b110001,
+                /* signalQuality= */ 1, createRadioMetadata(), /* vendorInfo= */ null);
+    }
+
+    private static RadioManager.FmBandConfig createBandConfig() {
+        return new RadioManager.FmBandConfig(new RadioManager.FmBandDescriptor(
+                RadioManager.REGION_ITU_1, RadioManager.BAND_FM, /* lowerLimit= */ 87500,
+                /* upperLimit= */ 108000, /* spacing= */ 200, /* stereo= */ true,
+                /* rds= */ false, /* ta= */ false, /* af= */ false, /* es= */ false));
+    }
+
+    private static RadioMetadata createRadioMetadata() {
+        RadioMetadata.Builder metadataBuilder = new RadioMetadata.Builder();
+        return metadataBuilder.putString(RadioMetadata.METADATA_KEY_ARTIST, "artistMock").build();
+    }
+}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
new file mode 100644
index 0000000..3119554
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.broadcastradio.aidl;
+
+import android.hardware.broadcastradio.AmFmBandRange;
+import android.hardware.broadcastradio.AmFmRegionConfig;
+import android.hardware.broadcastradio.DabTableEntry;
+import android.hardware.broadcastradio.IdentifierType;
+import android.hardware.broadcastradio.Properties;
+import android.hardware.broadcastradio.VendorKeyValue;
+import android.hardware.radio.Announcement;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.Map;
+
+public final class ConversionUtilsTest {
+
+    private static final int FM_LOWER_LIMIT = 87500;
+    private static final int FM_UPPER_LIMIT = 108000;
+    private static final int FM_SPACING = 200;
+    private static final int AM_LOWER_LIMIT = 540;
+    private static final int AM_UPPER_LIMIT = 1700;
+    private static final int AM_SPACING = 10;
+    private static final String DAB_ENTRY_LABEL_1 = "5A";
+    private static final int DAB_ENTRY_FREQUENCY_1 = 174928;
+    private static final String DAB_ENTRY_LABEL_2 = "12D";
+    private static final int DAB_ENTRY_FREQUENCY_2 = 229072;
+    private static final String VENDOR_INFO_KEY_1 = "vendorKey1";
+    private static final String VENDOR_INFO_VALUE_1 = "vendorValue1";
+    private static final String VENDOR_INFO_KEY_2 = "vendorKey2";
+    private static final String VENDOR_INFO_VALUE_2 = "vendorValue2";
+    private static final String TEST_SERVICE_NAME = "serviceMock";
+    private static final int TEST_ID = 1;
+    private static final String TEST_MAKER = "makerMock";
+    private static final String TEST_PRODUCT = "productMock";
+    private static final String TEST_VERSION = "versionMock";
+    private static final String TEST_SERIAL = "serialMock";
+
+    private static final int TEST_ENABLED_TYPE = Announcement.TYPE_EMERGENCY;
+    private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING;
+
+    private static final RadioManager.ModuleProperties MODULE_PROPERTIES =
+            convertToModuleProperties();
+    private static final Announcement ANNOUNCEMENT =
+            ConversionUtils.announcementFromHalAnnouncement(
+                    AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, TEST_ANNOUNCEMENT_FREQUENCY));
+
+    @Rule
+    public final Expect expect = Expect.create();
+
+    @Test
+    public void propertiesFromHalProperties_idsMatch() {
+        expect.withMessage("Properties id")
+                .that(MODULE_PROPERTIES.getId()).isEqualTo(TEST_ID);
+    }
+
+    @Test
+    public void propertiesFromHalProperties_serviceNamesMatch() {
+        expect.withMessage("Service name")
+                .that(MODULE_PROPERTIES.getServiceName()).isEqualTo(TEST_SERVICE_NAME);
+    }
+
+    @Test
+    public void propertiesFromHalProperties_implementorsMatch() {
+        expect.withMessage("Implementor")
+                .that(MODULE_PROPERTIES.getImplementor()).isEqualTo(TEST_MAKER);
+    }
+
+
+    @Test
+    public void propertiesFromHalProperties_productsMatch() {
+        expect.withMessage("Product")
+                .that(MODULE_PROPERTIES.getProduct()).isEqualTo(TEST_PRODUCT);
+    }
+
+    @Test
+    public void propertiesFromHalProperties_versionsMatch() {
+        expect.withMessage("Version")
+                .that(MODULE_PROPERTIES.getVersion()).isEqualTo(TEST_VERSION);
+    }
+
+    @Test
+    public void propertiesFromHalProperties_serialsMatch() {
+        expect.withMessage("Serial")
+                .that(MODULE_PROPERTIES.getSerial()).isEqualTo(TEST_SERIAL);
+    }
+
+    @Test
+    public void propertiesFromHalProperties_dabTableInfoMatch() {
+        Map<String, Integer> dabTableExpected = Map.of(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1,
+                DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2);
+
+        expect.withMessage("Supported program types")
+                .that(MODULE_PROPERTIES.getDabFrequencyTable())
+                .containsExactlyEntriesIn(dabTableExpected);
+    }
+
+    @Test
+    public void propertiesFromHalProperties_vendorInfoMatch() {
+        Map<String, String> vendorInfoExpected = Map.of(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1,
+                VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2);
+
+        expect.withMessage("Vendor info").that(MODULE_PROPERTIES.getVendorInfo())
+                .containsExactlyEntriesIn(vendorInfoExpected);
+    }
+
+    @Test
+    public void propertiesFromHalProperties_bandsMatch() {
+        RadioManager.BandDescriptor[] bands = MODULE_PROPERTIES.getBands();
+
+        expect.withMessage("Band descriptors").that(bands).hasLength(2);
+
+        expect.withMessage("FM band frequency lower limit")
+                .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
+        expect.withMessage("FM band frequency upper limit")
+                .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
+        expect.withMessage("FM band frequency spacing")
+                .that(bands[0].getSpacing()).isEqualTo(FM_SPACING);
+
+        expect.withMessage("AM band frequency lower limit")
+                .that(bands[1].getLowerLimit()).isEqualTo(AM_LOWER_LIMIT);
+        expect.withMessage("AM band frequency upper limit")
+                .that(bands[1].getUpperLimit()).isEqualTo(AM_UPPER_LIMIT);
+        expect.withMessage("AM band frequency spacing")
+                .that(bands[1].getSpacing()).isEqualTo(AM_SPACING);
+    }
+
+    @Test
+    public void announcementFromHalAnnouncement_typesMatch() {
+        expect.withMessage("Announcement type")
+                .that(ANNOUNCEMENT.getType()).isEqualTo(TEST_ENABLED_TYPE);
+    }
+
+    @Test
+    public void announcementFromHalAnnouncement_selectorsMatch() {
+        ProgramSelector.Identifier primaryIdExpected = new ProgramSelector.Identifier(
+                ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, TEST_ANNOUNCEMENT_FREQUENCY);
+
+        ProgramSelector selector = ANNOUNCEMENT.getSelector();
+
+        expect.withMessage("Primary id of announcement selector")
+                .that(selector.getPrimaryId()).isEqualTo(primaryIdExpected);
+        expect.withMessage("Secondary ids of announcement selector")
+                .that(selector.getSecondaryIds()).isEmpty();
+    }
+
+    @Test
+    public void announcementFromHalAnnouncement_VendorInfoMatch() {
+        expect.withMessage("Announcement vendor info")
+                .that(ANNOUNCEMENT.getVendorInfo()).isEmpty();
+    }
+
+    private static RadioManager.ModuleProperties convertToModuleProperties() {
+        AmFmRegionConfig amFmConfig = createAmFmRegionConfig();
+        DabTableEntry[] dabTableEntries = new DabTableEntry[]{
+                createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1),
+                createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)};
+        Properties properties = createHalProperties();
+
+        return ConversionUtils.propertiesFromHalProperties(TEST_ID, TEST_SERVICE_NAME, properties,
+                amFmConfig, dabTableEntries);
+    }
+
+    private static AmFmRegionConfig createAmFmRegionConfig() {
+        AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
+        amFmRegionConfig.ranges = new AmFmBandRange[]{
+                createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING),
+                createAmFmBandRange(AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING)};
+        return amFmRegionConfig;
+    }
+
+    private static AmFmBandRange createAmFmBandRange(int lowerBound, int upperBound, int spacing) {
+        AmFmBandRange bandRange = new AmFmBandRange();
+        bandRange.lowerBound = lowerBound;
+        bandRange.upperBound = upperBound;
+        bandRange.spacing = spacing;
+        bandRange.seekSpacing = bandRange.spacing;
+        return bandRange;
+    }
+
+    private static DabTableEntry createDabTableEntry(String label, int value) {
+        DabTableEntry dabTableEntry = new DabTableEntry();
+        dabTableEntry.label = label;
+        dabTableEntry.frequencyKhz = value;
+        return dabTableEntry;
+    }
+
+    private static Properties createHalProperties() {
+        Properties halProperties = new Properties();
+        halProperties.supportedIdentifierTypes = new int[]{IdentifierType.AMFM_FREQUENCY_KHZ,
+                IdentifierType.RDS_PI, IdentifierType.DAB_SID_EXT};
+        halProperties.maker = TEST_MAKER;
+        halProperties.product = TEST_PRODUCT;
+        halProperties.version = TEST_VERSION;
+        halProperties.serial = TEST_SERIAL;
+        halProperties.vendorInfo = new VendorKeyValue[]{
+                AidlTestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1),
+                AidlTestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2)};
+        return halProperties;
+    }
+}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index a767f83..48cfc87 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -82,6 +82,7 @@
 
     resource_dirs: ["res"],
     resource_zips: [":FrameworksCoreTests_apks_as_resources"],
+    java_resources: [":ApkVerityTestCertDer"],
 
     data: [
         ":BstatsTestApp",
diff --git a/core/tests/coretests/OWNERS b/core/tests/coretests/OWNERS
index 0fb0c30..e8c9fe7 100644
--- a/core/tests/coretests/OWNERS
+++ b/core/tests/coretests/OWNERS
@@ -1 +1,4 @@
 include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS
+
+per-file BinderTest.java = file:platform/frameworks/native:/libs/binder/OWNERS
+per-file ParcelTest.java = file:platform/frameworks/native:/libs/binder/OWNERS
diff --git a/core/tests/coretests/res/raw/fsverity_sig b/core/tests/coretests/res/raw/fsverity_sig
new file mode 100644
index 0000000..b2f335d
--- /dev/null
+++ b/core/tests/coretests/res/raw/fsverity_sig
Binary files differ
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 0f81896..7e875ad 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -18,6 +18,8 @@
 
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.BroadcastOptions;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -536,4 +538,40 @@
             Log.i("foo", "Unregister exception", e);
         }
     }
+
+    public void testBroadcastOption_interactive() throws Exception {
+        final BroadcastOptions options = BroadcastOptions.makeBasic();
+        options.setInteractiveBroadcast(true);
+        final Intent intent = makeBroadcastIntent(BROADCAST_REGISTERED);
+
+        try {
+            getContext().sendBroadcast(intent, null, options.toBundle());
+            fail("No exception thrown with BroadcastOptions.setInteractiveBroadcast(true)");
+        } catch (SecurityException se) {
+            // Expected, correct behavior - this case intentionally empty
+        } catch (Exception e) {
+            fail("Unexpected exception " + e.getMessage()
+                    + " thrown with BroadcastOptions.setInteractiveBroadcast(true)");
+        }
+    }
+
+    public void testBroadcastOption_interactive_PendingIntent() throws Exception {
+        final BroadcastOptions options = BroadcastOptions.makeBasic();
+        options.setInteractiveBroadcast(true);
+        final Intent intent = makeBroadcastIntent(BROADCAST_REGISTERED);
+        PendingIntent brPending = PendingIntent.getBroadcast(getContext(),
+                1, intent, PendingIntent.FLAG_IMMUTABLE);
+
+        try {
+            brPending.send(getContext(), 1, null, null, null, null, options.toBundle());
+            fail("No exception thrown with BroadcastOptions.setInteractiveBroadcast(true)");
+        } catch (SecurityException se) {
+            // Expected, correct behavior - this case intentionally empty
+        } catch (Exception e) {
+            fail("Unexpected exception " + e.getMessage()
+                    + " thrown with BroadcastOptions.setInteractiveBroadcast(true)");
+        } finally {
+            brPending.cancel();
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java b/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
new file mode 100644
index 0000000..67b24ec
--- /dev/null
+++ b/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
@@ -0,0 +1,277 @@
+/*
+ * 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 android.app.backup;
+
+import static android.app.backup.BackupRestoreEventLogger.OperationType.BACKUP;
+import static android.app.backup.BackupRestoreEventLogger.OperationType.RESTORE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.fail;
+
+import android.app.backup.BackupRestoreEventLogger.BackupRestoreDataType;
+import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupRestoreEventLoggerTest {
+    private static final int DATA_TYPES_ALLOWED = 15;
+
+    private static final String DATA_TYPE_1 = "data_type_1";
+    private static final String DATA_TYPE_2 = "data_type_2";
+    private static final String ERROR_1 = "error_1";
+    private static final String ERROR_2 = "error_2";
+    private static final String METADATA_1 = "metadata_1";
+    private static final String METADATA_2 = "metadata_2";
+
+    private BackupRestoreEventLogger mLogger;
+    private MessageDigest mHashDigest;
+
+    @Before
+    public void setUp() throws Exception {
+        mHashDigest = MessageDigest.getInstance("SHA-256");
+    }
+
+    @Test
+    public void testBackupLogger_rejectsRestoreLogs() {
+        mLogger = new BackupRestoreEventLogger(BACKUP);
+
+        assertThat(mLogger.logItemsRestored(DATA_TYPE_1, /* count */ 5)).isFalse();
+        assertThat(mLogger.logItemsRestoreFailed(DATA_TYPE_1, /* count */ 5, ERROR_1)).isFalse();
+        assertThat(mLogger.logRestoreMetadata(DATA_TYPE_1, /* metadata */ "metadata")).isFalse();
+    }
+
+    @Test
+    public void testRestoreLogger_rejectsBackupLogs() {
+        mLogger = new BackupRestoreEventLogger(RESTORE);
+
+        assertThat(mLogger.logItemsBackedUp(DATA_TYPE_1, /* count */ 5)).isFalse();
+        assertThat(mLogger.logItemsBackupFailed(DATA_TYPE_1, /* count */ 5, ERROR_1)).isFalse();
+        assertThat(mLogger.logBackupMetaData(DATA_TYPE_1, /* metadata */ "metadata")).isFalse();
+    }
+
+    @Test
+    public void testBackupLogger_onlyAcceptsAllowedNumberOfDataTypes() {
+        mLogger = new BackupRestoreEventLogger(BACKUP);
+
+        for (int i = 0; i < DATA_TYPES_ALLOWED; i++) {
+            String dataType = DATA_TYPE_1 + i;
+            assertThat(mLogger.logItemsBackedUp(dataType, /* count */ 5)).isTrue();
+            assertThat(mLogger.logItemsBackupFailed(dataType, /* count */ 5, /* error */ null))
+                    .isTrue();
+            assertThat(mLogger.logBackupMetaData(dataType, METADATA_1)).isTrue();
+        }
+
+        assertThat(mLogger.logItemsBackedUp(DATA_TYPE_2, /* count */ 5)).isFalse();
+        assertThat(mLogger.logItemsBackupFailed(DATA_TYPE_2, /* count */ 5, /* error */ null))
+                .isFalse();
+        assertThat(mLogger.logRestoreMetadata(DATA_TYPE_2, METADATA_1)).isFalse();
+        assertThat(getResultForDataTypeIfPresent(mLogger, DATA_TYPE_2)).isEqualTo(Optional.empty());
+    }
+
+    @Test
+    public void testRestoreLogger_onlyAcceptsAllowedNumberOfDataTypes() {
+        mLogger = new BackupRestoreEventLogger(RESTORE);
+
+        for (int i = 0; i < DATA_TYPES_ALLOWED; i++) {
+            String dataType = DATA_TYPE_1 + i;
+            assertThat(mLogger.logItemsRestored(dataType, /* count */ 5)).isTrue();
+            assertThat(mLogger.logItemsRestoreFailed(dataType, /* count */ 5, /* error */ null))
+                    .isTrue();
+            assertThat(mLogger.logRestoreMetadata(dataType, METADATA_1)).isTrue();
+        }
+
+        assertThat(mLogger.logItemsRestored(DATA_TYPE_2, /* count */ 5)).isFalse();
+        assertThat(mLogger.logItemsRestoreFailed(DATA_TYPE_2, /* count */ 5, /* error */ null))
+                .isFalse();
+        assertThat(mLogger.logRestoreMetadata(DATA_TYPE_2, METADATA_1)).isFalse();
+        assertThat(getResultForDataTypeIfPresent(mLogger, DATA_TYPE_2)).isEqualTo(Optional.empty());
+    }
+
+    @Test
+    public void testLogBackupMetadata_repeatedCalls_recordsLatestMetadataHash() {
+        mLogger = new BackupRestoreEventLogger(BACKUP);
+
+        mLogger.logBackupMetaData(DATA_TYPE_1, METADATA_1);
+        mLogger.logBackupMetaData(DATA_TYPE_1, METADATA_2);
+
+        byte[] recordedHash = getResultForDataType(mLogger, DATA_TYPE_1).getMetadataHash();
+        byte[] expectedHash = getMetaDataHash(METADATA_2);
+        assertThat(Arrays.equals(recordedHash, expectedHash)).isTrue();
+    }
+
+    @Test
+    public void testLogRestoreMetadata_repeatedCalls_recordsLatestMetadataHash() {
+        mLogger = new BackupRestoreEventLogger(RESTORE);
+
+        mLogger.logRestoreMetadata(DATA_TYPE_1, METADATA_1);
+        mLogger.logRestoreMetadata(DATA_TYPE_1, METADATA_2);
+
+        byte[] recordedHash = getResultForDataType(mLogger, DATA_TYPE_1).getMetadataHash();
+        byte[] expectedHash = getMetaDataHash(METADATA_2);
+        assertThat(Arrays.equals(recordedHash, expectedHash)).isTrue();
+    }
+
+    @Test
+    public void testLogItemsBackedUp_repeatedCalls_recordsTotalItems() {
+        mLogger = new BackupRestoreEventLogger(BACKUP);
+
+        int firstCount = 10;
+        int secondCount = 5;
+        mLogger.logItemsBackedUp(DATA_TYPE_1, firstCount);
+        mLogger.logItemsBackedUp(DATA_TYPE_1, secondCount);
+
+        int dataTypeCount = getResultForDataType(mLogger, DATA_TYPE_1).getSuccessCount();
+        assertThat(dataTypeCount).isEqualTo(firstCount + secondCount);
+    }
+
+    @Test
+    public void testLogItemsRestored_repeatedCalls_recordsTotalItems() {
+        mLogger = new BackupRestoreEventLogger(RESTORE);
+
+        int firstCount = 10;
+        int secondCount = 5;
+        mLogger.logItemsRestored(DATA_TYPE_1, firstCount);
+        mLogger.logItemsRestored(DATA_TYPE_1, secondCount);
+
+        int dataTypeCount = getResultForDataType(mLogger, DATA_TYPE_1).getSuccessCount();
+        assertThat(dataTypeCount).isEqualTo(firstCount + secondCount);
+    }
+
+    @Test
+    public void testLogItemsBackedUp_multipleDataTypes_recordsEachDataType() {
+        mLogger = new BackupRestoreEventLogger(BACKUP);
+
+        int firstCount = 10;
+        int secondCount = 5;
+        mLogger.logItemsBackedUp(DATA_TYPE_1, firstCount);
+        mLogger.logItemsBackedUp(DATA_TYPE_2, secondCount);
+
+        int firstDataTypeCount = getResultForDataType(mLogger, DATA_TYPE_1).getSuccessCount();
+        int secondDataTypeCount = getResultForDataType(mLogger, DATA_TYPE_2).getSuccessCount();
+        assertThat(firstDataTypeCount).isEqualTo(firstCount);
+        assertThat(secondDataTypeCount).isEqualTo(secondCount);
+    }
+
+    @Test
+    public void testLogItemsRestored_multipleDataTypes_recordsEachDataType() {
+        mLogger = new BackupRestoreEventLogger(RESTORE);
+
+        int firstCount = 10;
+        int secondCount = 5;
+        mLogger.logItemsRestored(DATA_TYPE_1, firstCount);
+        mLogger.logItemsRestored(DATA_TYPE_2, secondCount);
+
+        int firstDataTypeCount = getResultForDataType(mLogger, DATA_TYPE_1).getSuccessCount();
+        int secondDataTypeCount = getResultForDataType(mLogger, DATA_TYPE_2).getSuccessCount();
+        assertThat(firstDataTypeCount).isEqualTo(firstCount);
+        assertThat(secondDataTypeCount).isEqualTo(secondCount);
+    }
+
+    @Test
+    public void testLogItemsBackupFailed_repeatedCalls_recordsTotalItems() {
+        mLogger = new BackupRestoreEventLogger(BACKUP);
+
+        int firstCount = 10;
+        int secondCount = 5;
+        mLogger.logItemsBackupFailed(DATA_TYPE_1, firstCount, /* error */ null);
+        mLogger.logItemsBackupFailed(DATA_TYPE_1, secondCount, "error");
+
+        int dataTypeCount = getResultForDataType(mLogger, DATA_TYPE_1).getFailCount();
+        assertThat(dataTypeCount).isEqualTo(firstCount + secondCount);
+    }
+
+    @Test
+    public void testLogItemsRestoreFailed_repeatedCalls_recordsTotalItems() {
+        mLogger = new BackupRestoreEventLogger(RESTORE);
+
+        int firstCount = 10;
+        int secondCount = 5;
+        mLogger.logItemsRestoreFailed(DATA_TYPE_1, firstCount, /* error */ null);
+        mLogger.logItemsRestoreFailed(DATA_TYPE_1, secondCount, "error");
+
+        int dataTypeCount = getResultForDataType(mLogger, DATA_TYPE_1).getFailCount();
+        assertThat(dataTypeCount).isEqualTo(firstCount + secondCount);
+    }
+
+    @Test
+    public void testLogItemsBackupFailed_multipleErrors_recordsEachError() {
+        mLogger = new BackupRestoreEventLogger(BACKUP);
+
+        int firstCount = 10;
+        int secondCount = 5;
+        mLogger.logItemsBackupFailed(DATA_TYPE_1, firstCount, ERROR_1);
+        mLogger.logItemsBackupFailed(DATA_TYPE_1, secondCount, ERROR_2);
+
+        int firstErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1)
+                .getErrors().get(ERROR_1);
+        int secondErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1)
+                .getErrors().get(ERROR_2);
+        assertThat(firstErrorTypeCount).isEqualTo(firstCount);
+        assertThat(secondErrorTypeCount).isEqualTo(secondCount);
+    }
+
+    @Test
+    public void testLogItemsRestoreFailed_multipleErrors_recordsEachError() {
+        mLogger = new BackupRestoreEventLogger(RESTORE);
+
+        int firstCount = 10;
+        int secondCount = 5;
+        mLogger.logItemsRestoreFailed(DATA_TYPE_1, firstCount, ERROR_1);
+        mLogger.logItemsRestoreFailed(DATA_TYPE_1, secondCount, ERROR_2);
+
+        int firstErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1)
+                .getErrors().get(ERROR_1);
+        int secondErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1)
+                .getErrors().get(ERROR_2);
+        assertThat(firstErrorTypeCount).isEqualTo(firstCount);
+        assertThat(secondErrorTypeCount).isEqualTo(secondCount);
+    }
+
+    private static DataTypeResult getResultForDataType(BackupRestoreEventLogger logger,
+            @BackupRestoreDataType String dataType) {
+        Optional<DataTypeResult> result = getResultForDataTypeIfPresent(logger, dataType);
+        if (result.isEmpty()) {
+            fail("Failed to find result for data type: " + dataType);
+        }
+        return result.get();
+    }
+
+    private static Optional<DataTypeResult> getResultForDataTypeIfPresent(
+            BackupRestoreEventLogger logger, @BackupRestoreDataType String dataType) {
+        List<DataTypeResult> resultList = logger.getLoggingResults();
+        return resultList.stream().filter(
+                dataTypeResult -> dataTypeResult.getDataType().equals(dataType)).findAny();
+    }
+
+    private byte[] getMetaDataHash(String metaData) {
+        return mHashDigest.digest(metaData.getBytes(StandardCharsets.UTF_8));
+    }
+}
diff --git a/core/tests/coretests/src/android/app/backup/OWNERS b/core/tests/coretests/src/android/app/backup/OWNERS
new file mode 100644
index 0000000..53b6c78
--- /dev/null
+++ b/core/tests/coretests/src/android/app/backup/OWNERS
@@ -0,0 +1 @@
+include /services/backup/OWNERS
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index fdd278b..e2fe87b4 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -37,6 +37,13 @@
     private static final String INTERFACE_TOKEN_2 = "Another IBinder interface token";
 
     @Test
+    public void testIsForRpc() {
+        Parcel p = Parcel.obtain();
+        assertEquals(false, p.isForRpc());
+        p.recycle();
+    }
+
+    @Test
     public void testCallingWorkSourceUidAfterWrite() {
         Parcel p = Parcel.obtain();
         // Method does not throw if replaceCallingWorkSourceUid is called before requests headers
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index ddae652..19c2c61 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -193,11 +193,15 @@
         backspace(state, 0);
         state.assertEquals("|");
 
-        // Emoji modifier can be appended to the first emoji.
+        // Emoji modifier can be appended to each emoji.
         state.setByString("U+1F469 U+1F3FB U+200D U+1F4BC |");
         backspace(state, 0);
         state.assertEquals("|");
 
+        state.setByString("U+1F468 U+1F3FF U+200D U+2764 U+FE0F U+200D U+1F468 U+1F3FB |");
+        backspace(state, 0);
+        state.assertEquals("|");
+
         // End with ZERO WIDTH JOINER
         state.setByString("U+1F441 U+200D |");
         backspace(state, 0);
diff --git a/core/tests/coretests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java
new file mode 100644
index 0000000..79aeaa3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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 android.view.inputmethod;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.annotation.NonNull;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ParcelableHandwritingGestureTest {
+
+    @Test
+    public void testCreationFailWithNullPointerException() {
+        assertThrows(NullPointerException.class, () -> ParcelableHandwritingGesture.of(null));
+    }
+
+    @Test
+    public void testInvalidTypeHeader() {
+        Parcel parcel = null;
+        try {
+            parcel = Parcel.obtain();
+            // GESTURE_TYPE_NONE is not a supported header.
+            parcel.writeInt(HandwritingGesture.GESTURE_TYPE_NONE);
+            final Parcel initializedParcel = parcel;
+            assertThrows(UnsupportedOperationException.class,
+                    () -> ParcelableHandwritingGesture.CREATOR.createFromParcel(initializedParcel));
+        } finally {
+            if (parcel != null) {
+                parcel.recycle();
+            }
+        }
+    }
+
+    @Test
+    public void  testSelectGesture() {
+        verifyEqualityAfterUnparcel(new SelectGesture.Builder()
+                .setGranularity(HandwritingGesture.GRANULARITY_WORD)
+                .setSelectionArea(new RectF(1, 2, 3, 4))
+                .setFallbackText("")
+                .build());
+    }
+
+    @Test
+    public void  testSelectRangeGesture() {
+        verifyEqualityAfterUnparcel(new SelectRangeGesture.Builder()
+                .setGranularity(HandwritingGesture.GRANULARITY_WORD)
+                .setSelectionStartArea(new RectF(1, 2, 3, 4))
+                .setSelectionEndArea(new RectF(5, 6, 7, 8))
+                .setFallbackText("")
+                .build());
+    }
+
+    @Test
+    public void  testInsertGestureGesture() {
+        verifyEqualityAfterUnparcel(new InsertGesture.Builder()
+                .setTextToInsert("text")
+                .setInsertionPoint(new PointF(1, 1)).setFallbackText("")
+                .build());
+    }
+
+    @Test
+    public void  testDeleteGestureGesture() {
+        verifyEqualityAfterUnparcel(new DeleteGesture.Builder()
+                .setGranularity(HandwritingGesture.GRANULARITY_WORD)
+                .setDeletionArea(new RectF(1, 2, 3, 4))
+                .setFallbackText("")
+                .build());
+    }
+
+    @Test
+    public void  testDeleteRangeGestureGesture() {
+        verifyEqualityAfterUnparcel(new DeleteRangeGesture.Builder()
+                .setGranularity(HandwritingGesture.GRANULARITY_WORD)
+                .setDeletionStartArea(new RectF(1, 2, 3, 4))
+                .setDeletionEndArea(new RectF(5, 6, 7, 8))
+                .setFallbackText("")
+                .build());
+    }
+
+    @Test
+    public void  testRemoveSpaceGestureGesture() {
+        verifyEqualityAfterUnparcel(new RemoveSpaceGesture.Builder()
+                .setPoints(new PointF(1f, 2f), new PointF(3f, 4f))
+                .setFallbackText("")
+                .build());
+    }
+
+    @Test
+    public void  testJoinOrSplitGestureGesture() {
+        verifyEqualityAfterUnparcel(new JoinOrSplitGesture.Builder()
+                .setJoinOrSplitPoint(new PointF(1f, 2f))
+                .setFallbackText("")
+                .build());
+    }
+
+    static void verifyEqualityAfterUnparcel(@NonNull HandwritingGesture gesture) {
+        assertEquals(gesture, cloneViaParcel(ParcelableHandwritingGesture.of(gesture)).get());
+    }
+
+    private static ParcelableHandwritingGesture cloneViaParcel(
+            @NonNull ParcelableHandwritingGesture original) {
+        Parcel parcel = null;
+        try {
+            parcel = Parcel.obtain();
+            original.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            return ParcelableHandwritingGesture.CREATOR.createFromParcel(parcel);
+        } finally {
+            if (parcel != null) {
+                parcel.recycle();
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index d4a6632..95aa5d0 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.view.HandwritingDelegateConfiguration;
 import android.view.HandwritingInitiator;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -208,6 +209,30 @@
     }
 
     @Test
+    public void onTouchEvent_startHandwriting_delegate() {
+        int delegatorViewId = 234;
+        View delegatorView = new View(mContext);
+        delegatorView.setId(delegatorViewId);
+
+        mTestView.setHandwritingDelegateConfiguration(
+                new HandwritingDelegateConfiguration(
+                        delegatorViewId,
+                        () -> mHandwritingInitiator.onInputConnectionCreated(delegatorView)));
+
+        final int x1 = (sHwArea.left + sHwArea.right) / 2;
+        final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + mHandwritingSlop * 2;
+        final int y2 = y1;
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        verify(mHandwritingInitiator, times(1)).startHandwriting(delegatorView);
+    }
+
+    @Test
     public void onTouchEvent_notStartHandwriting_whenHandwritingNotAvailable() {
         final Rect rect = new Rect(600, 600, 900, 900);
         final View testView = createView(rect, true /* autoHandwritingEnabled */,
diff --git a/core/tests/coretests/src/com/android/internal/security/ContentSignerWrapper.java b/core/tests/coretests/src/com/android/internal/security/ContentSignerWrapper.java
new file mode 100644
index 0000000..0254afe
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/security/ContentSignerWrapper.java
@@ -0,0 +1,46 @@
+/*
+ * 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.internal.security;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.operator.ContentSigner;
+
+import java.io.OutputStream;
+
+/** A wrapper class of ContentSigner */
+class ContentSignerWrapper implements ContentSigner {
+    private final ContentSigner mSigner;
+
+    ContentSignerWrapper(ContentSigner wrapped) {
+        mSigner = wrapped;
+    }
+
+    @Override
+    public AlgorithmIdentifier getAlgorithmIdentifier() {
+        return mSigner.getAlgorithmIdentifier();
+    }
+
+    @Override
+    public OutputStream getOutputStream() {
+        return mSigner.getOutputStream();
+    }
+
+    @Override
+    public byte[] getSignature() {
+        return mSigner.getSignature();
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/security/OWNERS b/core/tests/coretests/src/com/android/internal/security/OWNERS
new file mode 100644
index 0000000..4f4d8d7
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/security/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+
+per-file VerityUtilsTest.java = file:platform/system/security:/fsverity/OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java b/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
new file mode 100644
index 0000000..1513654
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
@@ -0,0 +1,355 @@
+/*
+ * 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.internal.security;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.SignerInfoGenerator;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HexFormat;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VerityUtilsTest {
+    private static final byte[] SAMPLE_DIGEST = "12345678901234567890123456789012".getBytes();
+    private static final byte[] FORMATTED_SAMPLE_DIGEST = toFormattedDigest(SAMPLE_DIGEST);
+
+    KeyPair mKeyPair;
+    ContentSigner mContentSigner;
+    X509CertificateHolder mCertificateHolder;
+    byte[] mCertificateDerEncoded;
+
+    @Before
+    public void setUp() throws Exception {
+        mKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+        mContentSigner = newFsverityContentSigner(mKeyPair.getPrivate());
+        mCertificateHolder =
+                newX509CertificateHolder(mContentSigner, mKeyPair.getPublic(), "Someone");
+        mCertificateDerEncoded = mCertificateHolder.getEncoded();
+    }
+
+    @Test
+    public void testOnlyAcceptCorrectDigest() throws Exception {
+        byte[] pkcs7Signature =
+                generatePkcs7Signature(mContentSigner, mCertificateHolder, FORMATTED_SAMPLE_DIGEST);
+
+        byte[] anotherDigest = Arrays.copyOf(SAMPLE_DIGEST, SAMPLE_DIGEST.length);
+        anotherDigest[0] ^= (byte) 1;
+
+        assertTrue(verifySignature(pkcs7Signature, SAMPLE_DIGEST, mCertificateDerEncoded));
+        assertFalse(verifySignature(pkcs7Signature, anotherDigest, mCertificateDerEncoded));
+    }
+
+    @Test
+    public void testDigestWithWrongSize() throws Exception {
+        byte[] pkcs7Signature =
+                generatePkcs7Signature(mContentSigner, mCertificateHolder, FORMATTED_SAMPLE_DIGEST);
+        assertTrue(verifySignature(pkcs7Signature, SAMPLE_DIGEST, mCertificateDerEncoded));
+
+        byte[] digestTooShort = Arrays.copyOfRange(SAMPLE_DIGEST, 0, SAMPLE_DIGEST.length - 1);
+        assertFalse(verifySignature(pkcs7Signature, digestTooShort, mCertificateDerEncoded));
+
+        byte[] digestTooLong = Arrays.copyOfRange(SAMPLE_DIGEST, 0, SAMPLE_DIGEST.length + 1);
+        assertFalse(verifySignature(pkcs7Signature, digestTooLong, mCertificateDerEncoded));
+    }
+
+    @Test
+    public void testOnlyAcceptGoodSignature() throws Exception {
+        byte[] pkcs7Signature =
+                generatePkcs7Signature(mContentSigner, mCertificateHolder, FORMATTED_SAMPLE_DIGEST);
+
+        byte[] anotherDigest = Arrays.copyOf(SAMPLE_DIGEST, SAMPLE_DIGEST.length);
+        anotherDigest[0] ^= (byte) 1;
+        byte[] anotherPkcs7Signature =
+                generatePkcs7Signature(
+                        mContentSigner, mCertificateHolder, toFormattedDigest(anotherDigest));
+
+        assertTrue(verifySignature(pkcs7Signature, SAMPLE_DIGEST, mCertificateDerEncoded));
+        assertFalse(verifySignature(anotherPkcs7Signature, SAMPLE_DIGEST, mCertificateDerEncoded));
+    }
+
+    @Test
+    public void testOnlyValidCertCanVerify() throws Exception {
+        byte[] pkcs7Signature =
+                generatePkcs7Signature(mContentSigner, mCertificateHolder, FORMATTED_SAMPLE_DIGEST);
+
+        var wrongKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+        var wrongContentSigner = newFsverityContentSigner(wrongKeyPair.getPrivate());
+        var wrongCertificateHolder =
+                newX509CertificateHolder(wrongContentSigner, wrongKeyPair.getPublic(), "Not Me");
+        byte[] wrongCertificateDerEncoded = wrongCertificateHolder.getEncoded();
+
+        assertFalse(verifySignature(pkcs7Signature, SAMPLE_DIGEST, wrongCertificateDerEncoded));
+    }
+
+    @Test
+    public void testRejectSignatureWithContent() throws Exception {
+        CMSSignedDataGenerator generator =
+                newFsveritySignedDataGenerator(mContentSigner, mCertificateHolder);
+        byte[] pkcs7SignatureNonDetached =
+                generatePkcs7SignatureInternal(
+                        generator, FORMATTED_SAMPLE_DIGEST, /* encapsulate */ true);
+
+        assertFalse(
+                verifySignature(pkcs7SignatureNonDetached, SAMPLE_DIGEST, mCertificateDerEncoded));
+    }
+
+    @Test
+    public void testRejectSignatureWithCertificate() throws Exception {
+        CMSSignedDataGenerator generator =
+                newFsveritySignedDataGenerator(mContentSigner, mCertificateHolder);
+        generator.addCertificate(mCertificateHolder);
+        byte[] pkcs7Signature =
+                generatePkcs7SignatureInternal(
+                        generator, FORMATTED_SAMPLE_DIGEST, /* encapsulate */ false);
+
+        assertFalse(
+                verifySignature(pkcs7Signature, SAMPLE_DIGEST, mCertificateDerEncoded));
+    }
+
+    @Ignore("No easy way to construct test data")
+    @Test
+    public void testRejectSignatureWithCRL() throws Exception {
+        CMSSignedDataGenerator generator =
+                newFsveritySignedDataGenerator(mContentSigner, mCertificateHolder);
+
+        // The current bouncycastle version does not have an easy way to generate a CRL.
+        // TODO: enable the test once this is doable, e.g. with X509v2CRLBuilder.
+        // generator.addCRL(new X509CRLHolder(CertificateList.getInstance(new DERSequence(...))));
+        byte[] pkcs7Signature =
+                generatePkcs7SignatureInternal(
+                        generator, FORMATTED_SAMPLE_DIGEST, /* encapsulate */ false);
+
+        assertFalse(
+                verifySignature(pkcs7Signature, SAMPLE_DIGEST, mCertificateDerEncoded));
+    }
+
+    @Test
+    public void testRejectUnsupportedSignatureAlgorithms() throws Exception {
+        var contentSigner = newFsverityContentSigner(mKeyPair.getPrivate(), "MD5withRSA", null);
+        var certificateHolder =
+                newX509CertificateHolder(contentSigner, mKeyPair.getPublic(), "Someone");
+        byte[] pkcs7Signature =
+                generatePkcs7Signature(contentSigner, certificateHolder, FORMATTED_SAMPLE_DIGEST);
+
+        assertFalse(verifySignature(pkcs7Signature, SAMPLE_DIGEST, certificateHolder.getEncoded()));
+    }
+
+    @Test
+    public void testRejectUnsupportedDigestAlgorithm() throws Exception {
+        CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
+        generator.addSignerInfoGenerator(
+                newSignerInfoGenerator(
+                        mContentSigner,
+                        mCertificateHolder,
+                        OIWObjectIdentifiers.idSHA1,
+                        true)); // directSignature
+        byte[] pkcs7Signature =
+                generatePkcs7SignatureInternal(
+                        generator, FORMATTED_SAMPLE_DIGEST, /* encapsulate */ false);
+
+        assertFalse(verifySignature(pkcs7Signature, SAMPLE_DIGEST, mCertificateDerEncoded));
+    }
+
+    @Test
+    public void testRejectAnySignerInfoAttributes() throws Exception {
+        var generator = new CMSSignedDataGenerator();
+        generator.addSignerInfoGenerator(
+                newSignerInfoGenerator(
+                        mContentSigner,
+                        mCertificateHolder,
+                        NISTObjectIdentifiers.id_sha256,
+                        false)); // directSignature
+        byte[] pkcs7Signature =
+                generatePkcs7SignatureInternal(
+                        generator, FORMATTED_SAMPLE_DIGEST, /* encapsulate */ false);
+
+        assertFalse(verifySignature(pkcs7Signature, SAMPLE_DIGEST, mCertificateDerEncoded));
+    }
+
+    @Test
+    public void testSignatureGeneratedExternally() throws Exception {
+        var context = InstrumentationRegistry.getInstrumentation().getContext();
+        byte[] cert = getClass().getClassLoader().getResourceAsStream("ApkVerityTestCert.der")
+                .readAllBytes();
+        // The signature is generated by:
+        //   fsverity sign <(echo -n fs-verity) fsverity_sig --key=ApkVerityTestKey.pem \
+        //   --cert=ApkVerityTestCert.pem
+        byte[] sig = context.getResources().openRawResource(R.raw.fsverity_sig).readAllBytes();
+        // The fs-verity digest is generated by:
+        //   fsverity digest --compact <(echo -n fs-verity)
+        byte[] digest = HexFormat.of().parseHex(
+                "3d248ca542a24fc62d1c43b916eae5016878e2533c88238480b26128a1f1af95");
+
+        assertTrue(verifySignature(sig, digest, cert));
+    }
+
+    private static boolean verifySignature(
+            byte[] pkcs7Signature, byte[] fsverityDigest, byte[] certificateDerEncoded) {
+        return VerityUtils.verifyPkcs7DetachedSignature(
+                pkcs7Signature, fsverityDigest, new ByteArrayInputStream(certificateDerEncoded));
+    }
+
+    private static byte[] toFormattedDigest(byte[] digest) {
+        return VerityUtils.toFormattedDigest(digest);
+    }
+
+    private static byte[] generatePkcs7Signature(
+            ContentSigner contentSigner, X509CertificateHolder certificateHolder, byte[] signedData)
+            throws IOException, CMSException, OperatorCreationException {
+        CMSSignedDataGenerator generator =
+                newFsveritySignedDataGenerator(contentSigner, certificateHolder);
+        return generatePkcs7SignatureInternal(generator, signedData, /* encapsulate */ false);
+    }
+
+    private static byte[] generatePkcs7SignatureInternal(
+            CMSSignedDataGenerator generator, byte[] signedData, boolean encapsulate)
+            throws IOException, CMSException, OperatorCreationException {
+        CMSSignedData cmsSignedData =
+                generator.generate(new CMSProcessableByteArray(signedData), encapsulate);
+        return cmsSignedData.toASN1Structure().getEncoded(ASN1Encoding.DL);
+    }
+
+    private static CMSSignedDataGenerator newFsveritySignedDataGenerator(
+            ContentSigner contentSigner, X509CertificateHolder certificateHolder)
+            throws IOException, CMSException, OperatorCreationException {
+        var generator = new CMSSignedDataGenerator();
+        generator.addSignerInfoGenerator(
+                newSignerInfoGenerator(
+                        contentSigner,
+                        certificateHolder,
+                        NISTObjectIdentifiers.id_sha256,
+                        true)); // directSignature
+        return generator;
+    }
+
+    private static SignerInfoGenerator newSignerInfoGenerator(
+            ContentSigner contentSigner,
+            X509CertificateHolder certificateHolder,
+            ASN1ObjectIdentifier digestAlgorithmId,
+            boolean directSignature)
+            throws IOException, CMSException, OperatorCreationException {
+        var provider =
+                new BcDigestCalculatorProvider() {
+                    /**
+                     * Allow the caller to override the digest algorithm, especially when the
+                     * default does not work (i.e. BcDigestCalculatorProvider could return null).
+                     *
+                     * <p>For example, the current fs-verity signature has to use rsaEncryption for
+                     * the signature algorithm, but BcDigestCalculatorProvider will return null,
+                     * thus we need a way to override.
+                     *
+                     * <p>TODO: After bouncycastle 1.70, we can remove this override and just use
+                     * {@code JcaSignerInfoGeneratorBuilder#setContentDigest}.
+                     */
+                    @Override
+                    public DigestCalculator get(AlgorithmIdentifier algorithm)
+                            throws OperatorCreationException {
+                        return super.get(new AlgorithmIdentifier(digestAlgorithmId));
+                    }
+                };
+        var builder =
+                new JcaSignerInfoGeneratorBuilder(provider).setDirectSignature(directSignature);
+        return builder.build(contentSigner, certificateHolder);
+    }
+
+    private static ContentSigner newFsverityContentSigner(PrivateKey privateKey)
+            throws OperatorCreationException {
+        // fs-verity expects the signature to have rsaEncryption as the exact algorithm, so
+        // override the default.
+        return newFsverityContentSigner(
+                privateKey, "SHA256withRSA", PKCSObjectIdentifiers.rsaEncryption);
+    }
+
+    private static ContentSigner newFsverityContentSigner(
+            PrivateKey privateKey,
+            String signatureAlgorithm,
+            ASN1ObjectIdentifier signatureAlgorithmIdOverride)
+            throws OperatorCreationException {
+        if (signatureAlgorithmIdOverride != null) {
+            return new ContentSignerWrapper(
+                    new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey)) {
+                @Override
+                public AlgorithmIdentifier getAlgorithmIdentifier() {
+                    return new AlgorithmIdentifier(signatureAlgorithmIdOverride);
+                }
+            };
+        } else {
+            return new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey);
+        }
+    }
+
+    private static X509CertificateHolder newX509CertificateHolder(
+            ContentSigner contentSigner, PublicKey publicKey, String name) {
+        // Time doesn't really matter, as we only care about the key.
+        Instant now = Instant.now();
+
+        return new X509v3CertificateBuilder(
+                        new X500Name("CN=Issuer " + name),
+                        /* serial= */ BigInteger.valueOf(now.getEpochSecond()),
+                        new Date(now.minus(Duration.ofDays(1)).toEpochMilli()),
+                        new Date(now.plus(Duration.ofDays(1)).toEpochMilli()),
+                        new X500Name("CN=Subject " + name),
+                        SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()))
+                .build(contentSigner);
+    }
+}
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index 0f82c8f..889edb3 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -311,11 +311,11 @@
     /**
      * Contains the result of a PixelCopy request
      */
-    public static final class CopyResult {
+    public static final class Result {
         private int mStatus;
         private Bitmap mBitmap;
 
-        private CopyResult(@CopyResultStatus int status, Bitmap bitmap) {
+        private Result(@CopyResultStatus int status, Bitmap bitmap) {
             mStatus = status;
             mBitmap = bitmap;
         }
@@ -335,8 +335,8 @@
 
         /**
          * If the PixelCopy {@link Request} was given a destination bitmap with
-         * {@link Request#setDestinationBitmap(Bitmap)} then the returned bitmap will be the same
-         * as the one given. If no destination bitmap was provided, then this
+         * {@link Request.Builder#setDestinationBitmap(Bitmap)} then the returned bitmap will be
+         * the same as the one given. If no destination bitmap was provided, then this
          * will contain the automatically allocated Bitmap to hold the result.
          *
          * @return the Bitmap the copy request was stored in.
@@ -349,66 +349,199 @@
     }
 
     /**
-     * A builder to create the complete PixelCopy request, which is then executed by calling
-     * {@link #request()}
+     * Represents a PixelCopy request.
+     *
+     * To create a copy request, use either of the PixelCopy.Request.ofWindow or
+     * PixelCopy.Request.ofSurface factories to create a {@link Request.Builder} for the
+     * given source content. After setting any optional parameters, such as
+     * {@link Builder#setSourceRect(Rect)}, build the request with {@link Builder#build()} and
+     * then execute it with {@link PixelCopy#request(Request)}
      */
     public static final class Request {
+        private final Surface mSource;
+        private final Consumer<Result> mListener;
+        private final Executor mListenerThread;
+        private final Rect mSourceInsets;
+        private Rect mSrcRect;
+        private Bitmap mDest;
+
         private Request(Surface source, Rect sourceInsets, Executor listenerThread,
-                        Consumer<CopyResult> listener) {
+                        Consumer<Result> listener) {
             this.mSource = source;
             this.mSourceInsets = sourceInsets;
             this.mListenerThread = listenerThread;
             this.mListener = listener;
         }
 
-        private final Surface mSource;
-        private final Consumer<CopyResult> mListener;
-        private final Executor mListenerThread;
-        private final Rect mSourceInsets;
-        private Rect mSrcRect;
-        private Bitmap mDest;
-
         /**
-         * Sets the region of the source to copy from. By default, the entire source is copied to
-         * the output. If only a subset of the source is necessary to be copied, specifying a
-         * srcRect will improve performance by reducing
-         * the amount of data being copied.
-         *
-         * @param srcRect The area of the source to read from. Null or empty will be treated to
-         *                mean the entire source
-         * @return this
+         * A builder to create the complete PixelCopy request, which is then executed by calling
+         * {@link #request(Request)} with the built request returned from {@link #build()}
          */
-        public @NonNull Request setSourceRect(@Nullable Rect srcRect) {
-            this.mSrcRect = srcRect;
-            return this;
-        }
+        public static final class Builder {
+            private Request mRequest;
 
-        /**
-         * Specifies the output bitmap in which to store the result. By default, a Bitmap of format
-         * {@link android.graphics.Bitmap.Config#ARGB_8888} with a width & height matching that
-         * of the {@link #setSourceRect(Rect) source area} will be created to place the result.
-         *
-         * @param destination The bitmap to store the result, or null to have a bitmap
-         *                    automatically created of the appropriate size. If not null, must not
-         *                    be {@link Bitmap#isRecycled() recycled} and must be
-         *                    {@link Bitmap#isMutable() mutable}.
-         * @return this
-         */
-        public @NonNull Request setDestinationBitmap(@Nullable Bitmap destination) {
-            if (destination != null) {
-                validateBitmapDest(destination);
+            private Builder(Request request) {
+                mRequest = request;
             }
-            this.mDest = destination;
-            return this;
+
+            private void requireNotBuilt() {
+                if (mRequest == null) {
+                    throw new IllegalStateException("build() already called on this builder");
+                }
+            }
+
+            /**
+             * Sets the region of the source to copy from. By default, the entire source is copied
+             * to the output. If only a subset of the source is necessary to be copied, specifying
+             * a srcRect will improve performance by reducing
+             * the amount of data being copied.
+             *
+             * @param srcRect The area of the source to read from. Null or empty will be treated to
+             *                mean the entire source
+             * @return this
+             */
+            public @NonNull Builder setSourceRect(@Nullable Rect srcRect) {
+                requireNotBuilt();
+                mRequest.mSrcRect = srcRect;
+                return this;
+            }
+
+            /**
+             * Specifies the output bitmap in which to store the result. By default, a Bitmap of
+             * format {@link android.graphics.Bitmap.Config#ARGB_8888} with a width & height
+             * matching that of the {@link #setSourceRect(Rect) source area} will be created to
+             * place the result.
+             *
+             * @param destination The bitmap to store the result, or null to have a bitmap
+             *                    automatically created of the appropriate size. If not null, must
+             *                    not be {@link Bitmap#isRecycled() recycled} and must be
+             *                    {@link Bitmap#isMutable() mutable}.
+             * @return this
+             */
+            public @NonNull Builder setDestinationBitmap(@Nullable Bitmap destination) {
+                requireNotBuilt();
+                if (destination != null) {
+                    validateBitmapDest(destination);
+                }
+                mRequest.mDest = destination;
+                return this;
+            }
+
+            /**
+             * @return The built {@link PixelCopy.Request}
+             */
+            public @NonNull Request build() {
+                requireNotBuilt();
+                Request ret = mRequest;
+                mRequest = null;
+                return ret;
+            }
         }
 
         /**
-         * Executes the request.
+         * Creates a PixelCopy request for the given {@link Window}
+         * @param source The Window to copy from
+         * @param callbackExecutor The executor to run the callback on
+         * @param listener The callback for when the copy request is completed
+         * @return A {@link Builder} builder to set the optional params & execute the request
+         */
+        public static @NonNull Builder ofWindow(@NonNull Window source,
+                                                @NonNull Executor callbackExecutor,
+                                                @NonNull Consumer<Result> listener) {
+            final Rect insets = new Rect();
+            final Surface surface = sourceForWindow(source, insets);
+            return new Builder(new Request(surface, insets, callbackExecutor, listener));
+        }
+
+        /**
+         * Creates a PixelCopy request for the {@link Window} that the given {@link View} is
+         * attached to.
+         *
+         * Note that this copy request is not cropped to the area the View occupies by default. If
+         * that behavior is desired, use {@link View#getLocationInWindow(int[])} combined with
+         * {@link Builder#setSourceRect(Rect)} to set a crop area to restrict the copy operation.
+         *
+         * @param source A View that {@link View#isAttachedToWindow() is attached} to a window that
+         *               will be used to retrieve the window to copy from.
+         * @param callbackExecutor The executor to run the callback on
+         * @param listener The callback for when the copy request is completed
+         * @return A {@link Builder} builder to set the optional params & execute the request
+         */
+        public static @NonNull Builder ofWindow(@NonNull View source,
+                                                @NonNull Executor callbackExecutor,
+                                                @NonNull Consumer<Result> listener) {
+            if (source == null || !source.isAttachedToWindow()) {
+                throw new IllegalArgumentException(
+                        "View must not be null & must be attached to window");
+            }
+            final Rect insets = new Rect();
+            Surface surface = null;
+            final ViewRootImpl root = source.getViewRootImpl();
+            if (root != null) {
+                surface = root.mSurface;
+                insets.set(root.mWindowAttributes.surfaceInsets);
+            }
+            if (surface == null || !surface.isValid()) {
+                throw new IllegalArgumentException(
+                        "Window doesn't have a backing surface!");
+            }
+            return new Builder(new Request(surface, insets, callbackExecutor, listener));
+        }
+
+        /**
+         * Creates a PixelCopy request for the given {@link Surface}
+         *
+         * @param source The Surface to copy from. Must be {@link Surface#isValid() valid}.
+         * @param callbackExecutor The executor to run the callback on
+         * @param listener The callback for when the copy request is completed
+         * @return A {@link Builder} builder to set the optional params & execute the request
+         */
+        public static @NonNull Builder ofSurface(@NonNull Surface source,
+                                                 @NonNull Executor callbackExecutor,
+                                                 @NonNull Consumer<Result> listener) {
+            if (source == null || !source.isValid()) {
+                throw new IllegalArgumentException("Source must not be null & must be valid");
+            }
+            return new Builder(new Request(source, null, callbackExecutor, listener));
+        }
+
+        /**
+         * Creates a PixelCopy request for the {@link Surface} belonging to the
+         * given {@link SurfaceView}
+         *
+         * @param source The SurfaceView to copy from. The backing surface must be
+         *               {@link Surface#isValid() valid}
+         * @param callbackExecutor The executor to run the callback on
+         * @param listener The callback for when the copy request is completed
+         * @return A {@link Builder} builder to set the optional params & execute the request
+         */
+        public static @NonNull Builder ofSurface(@NonNull SurfaceView source,
+                                                 @NonNull Executor callbackExecutor,
+                                                 @NonNull Consumer<Result> listener) {
+            return ofSurface(source.getHolder().getSurface(), callbackExecutor, listener);
+        }
+
+        /**
+         * @return The destination bitmap as set by {@link Builder#setDestinationBitmap(Bitmap)}
+         */
+        public @Nullable Bitmap getDestinationBitmap() {
+            return mDest;
+        }
+
+        /**
+         * @return The source rect to copy from as set by {@link Builder#setSourceRect(Rect)}
+         */
+        public @Nullable Rect getSourceRect() {
+            return mSrcRect;
+        }
+
+        /**
+         * @hide
          */
         public void request() {
             if (!mSource.isValid()) {
                 mListenerThread.execute(() -> mListener.accept(
-                        new CopyResult(ERROR_SOURCE_INVALID, null)));
+                        new Result(ERROR_SOURCE_INVALID, null)));
                 return;
             }
             HardwareRenderer.copySurfaceInto(mSource, new HardwareRenderer.CopyRequest(
@@ -416,93 +549,18 @@
                 @Override
                 public void onCopyFinished(int result) {
                     mListenerThread.execute(() -> mListener.accept(
-                            new CopyResult(result, mDestinationBitmap)));
+                            new Result(result, mDestinationBitmap)));
                 }
             });
         }
     }
 
     /**
-     * Creates a PixelCopy request for the given {@link Window}
-     * @param source The Window to copy from
-     * @param callbackExecutor The executor to run the callback on
-     * @param listener The callback for when the copy request is completed
-     * @return A {@link Request} builder to set the optional params & execute the request
+     * Executes the pixel copy request
+     * @param request The request to execute
      */
-    public static @NonNull Request ofWindow(@NonNull Window source,
-                                            @NonNull Executor callbackExecutor,
-                                            @NonNull Consumer<CopyResult> listener) {
-        final Rect insets = new Rect();
-        final Surface surface = sourceForWindow(source, insets);
-        return new Request(surface, insets, callbackExecutor, listener);
-    }
-
-    /**
-     * Creates a PixelCopy request for the {@link Window} that the given {@link View} is
-     * attached to.
-     *
-     * Note that this copy request is not cropped to the area the View occupies by default. If that
-     * behavior is desired, use {@link View#getLocationInWindow(int[])} combined with
-     * {@link Request#setSourceRect(Rect)} to set a crop area to restrict the copy operation.
-     *
-     * @param source A View that {@link View#isAttachedToWindow() is attached} to a window that
-     *               will be used to retrieve the window to copy from.
-     * @param callbackExecutor The executor to run the callback on
-     * @param listener The callback for when the copy request is completed
-     * @return A {@link Request} builder to set the optional params & execute the request
-     */
-    public static @NonNull Request ofWindow(@NonNull View source,
-                                            @NonNull Executor callbackExecutor,
-                                            @NonNull Consumer<CopyResult> listener) {
-        if (source == null || !source.isAttachedToWindow()) {
-            throw new IllegalArgumentException(
-                    "View must not be null & must be attached to window");
-        }
-        final Rect insets = new Rect();
-        Surface surface = null;
-        final ViewRootImpl root = source.getViewRootImpl();
-        if (root != null) {
-            surface = root.mSurface;
-            insets.set(root.mWindowAttributes.surfaceInsets);
-        }
-        if (surface == null || !surface.isValid()) {
-            throw new IllegalArgumentException(
-                    "Window doesn't have a backing surface!");
-        }
-        return new Request(surface, insets, callbackExecutor, listener);
-    }
-
-    /**
-     * Creates a PixelCopy request for the given {@link Surface}
-     *
-     * @param source The Surface to copy from. Must be {@link Surface#isValid() valid}.
-     * @param callbackExecutor The executor to run the callback on
-     * @param listener The callback for when the copy request is completed
-     * @return A {@link Request} builder to set the optional params & execute the request
-     */
-    public static @NonNull Request ofSurface(@NonNull Surface source,
-                                             @NonNull Executor callbackExecutor,
-                                             @NonNull Consumer<CopyResult> listener) {
-        if (source == null || !source.isValid()) {
-            throw new IllegalArgumentException("Source must not be null & must be valid");
-        }
-        return new Request(source, null, callbackExecutor, listener);
-    }
-
-    /**
-     * Creates a PixelCopy request for the {@link Surface} belonging to the
-     * given {@link SurfaceView}
-     *
-     * @param source The SurfaceView to copy from. The backing surface must be
-     *               {@link Surface#isValid() valid}
-     * @param callbackExecutor The executor to run the callback on
-     * @param listener The callback for when the copy request is completed
-     * @return A {@link Request} builder to set the optional params & execute the request
-     */
-    public static @NonNull Request ofSurface(@NonNull SurfaceView source,
-                                             @NonNull Executor callbackExecutor,
-                                             @NonNull Consumer<CopyResult> listener) {
-        return ofSurface(source.getHolder().getSurface(), callbackExecutor, listener);
+    public static void request(@NonNull Request request) {
+        request.request();
     }
 
     private PixelCopy() {}
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 30c3d50..df5f921 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -23,6 +23,10 @@
          TODO(b/238217847): This config is temporary until we refactor the base WMComponent. -->
     <bool name="config_registerShellTaskOrganizerOnInit">true</bool>
 
+    <!-- Determines whether to register the shell transitions on init.
+         TODO(b/238217847): This config is temporary until we refactor the base WMComponent. -->
+    <bool name="config_registerShellTransitionsOnInit">true</bool>
+
     <!-- Animation duration for PIP when entering. -->
     <integer name="config_pipEnterAnimationDuration">425</integer>
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 48c5f64..bd2ea9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -41,7 +41,6 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
@@ -122,7 +121,7 @@
 
     /** Until all users are converted, we may have mixed-use (eg. Car). */
     private boolean isUsingShellTransitions() {
-        return mTaskViewTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS;
+        return mTaskViewTransitions != null && mTaskViewTransitions.isEnabled();
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
index 83335ac..07d5012 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
@@ -87,6 +87,10 @@
         // Note: Don't unregister handler since this is a singleton with lifetime bound to Shell
     }
 
+    boolean isEnabled() {
+        return mTransitions.isRegistered();
+    }
+
     /**
      * Looks through the pending transitions for one matching `taskView`.
      * @param taskView the pending transition should be for this.
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 0bb76b9..bb7c4134 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
@@ -38,10 +38,10 @@
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
+import android.view.inputmethod.InputMethodManagerGlobal;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.inputmethod.IInputMethodManagerGlobal;
 import com.android.wm.shell.sysui.ShellInit;
 
 import java.util.ArrayList;
@@ -515,7 +515,7 @@
     void removeImeSurface() {
         // Remove the IME surface to make the insets invisible for
         // non-client controlled insets.
-        IInputMethodManagerGlobal.removeImeSurface(
+        InputMethodManagerGlobal.removeImeSurface(
                 e -> Slog.e(TAG, "Failed to remove IME surface.", e));
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index c2ad1a9..5b7d141 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -69,6 +69,7 @@
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 
 import java.io.PrintWriter;
+import java.util.function.Consumer;
 
 /**
  * Records and handles layout of splits. Helps to calculate proper bounds when configuration or
@@ -599,7 +600,7 @@
 
     /** Swich both surface position with animation. */
     public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1,
-            SurfaceControl leash2, Runnable finishCallback) {
+            SurfaceControl leash2, Consumer<Rect> finishCallback) {
         final boolean isLandscape = isLandscape();
         final Rect insets = getDisplayInsets(mContext);
         insets.set(isLandscape ? insets.left : 0, isLandscape ? 0 : insets.top,
@@ -617,18 +618,13 @@
         distBounds1.offset(-mRootBounds.left, -mRootBounds.top);
         distBounds2.offset(-mRootBounds.left, -mRootBounds.top);
         distDividerBounds.offset(-mRootBounds.left, -mRootBounds.top);
-        // DO NOT move to insets area for smooth animation.
-        distBounds1.set(distBounds1.left, distBounds1.top,
-                distBounds1.right - insets.right, distBounds1.bottom - insets.bottom);
-        distBounds2.set(distBounds2.left + insets.left, distBounds2.top + insets.top,
-                distBounds2.right, distBounds2.bottom);
 
         ValueAnimator animator1 = moveSurface(t, leash1, getRefBounds1(), distBounds1,
-                false /* alignStart */);
+                -insets.left, -insets.top);
         ValueAnimator animator2 = moveSurface(t, leash2, getRefBounds2(), distBounds2,
-                true /* alignStart */);
+                insets.left, insets.top);
         ValueAnimator animator3 = moveSurface(t, getDividerLeash(), getRefDividerBounds(),
-                distDividerBounds, true /* alignStart */);
+                distDividerBounds, 0 /* offsetX */, 0 /* offsetY */);
 
         AnimatorSet set = new AnimatorSet();
         set.playTogether(animator1, animator2, animator3);
@@ -638,14 +634,14 @@
             public void onAnimationEnd(Animator animation) {
                 mDividePosition = dividerPos;
                 updateBounds(mDividePosition);
-                finishCallback.run();
+                finishCallback.accept(insets);
             }
         });
         set.start();
     }
 
     private ValueAnimator moveSurface(SurfaceControl.Transaction t, SurfaceControl leash,
-            Rect start, Rect end, boolean alignStart) {
+            Rect start, Rect end, float offsetX, float offsetY) {
         Rect tempStart = new Rect(start);
         Rect tempEnd = new Rect(end);
         final float diffX = tempEnd.left - tempStart.left;
@@ -661,15 +657,15 @@
             final float distY = tempStart.top + scale * diffY;
             final int width = (int) (tempStart.width() + scale * diffWidth);
             final int height = (int) (tempStart.height() + scale * diffHeight);
-            if (alignStart) {
+            if (offsetX == 0 && offsetY == 0) {
                 t.setPosition(leash, distX, distY);
                 t.setWindowCrop(leash, width, height);
             } else {
-                final int offsetX = width - tempStart.width();
-                final int offsetY = height - tempStart.height();
-                t.setPosition(leash, distX + offsetX, distY + offsetY);
+                final int diffOffsetX = (int) (scale * offsetX);
+                final int diffOffsetY = (int) (scale * offsetY);
+                t.setPosition(leash, distX + diffOffsetX, distY + diffOffsetY);
                 mTempRect.set(0, 0, width, height);
-                mTempRect.offsetTo(-offsetX, -offsetY);
+                mTempRect.offsetTo(-diffOffsetX, -diffOffsetY);
                 t.setCrop(leash, mTempRect);
             }
             t.apply();
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 64dbfbb..8b8e192 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
@@ -507,6 +507,10 @@
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler,
             @ShellAnimationThread ShellExecutor animExecutor) {
+        if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) {
+            // TODO(b/238217847): Force override shell init if registration is disabled
+            shellInit = new ShellInit(mainExecutor);
+        }
         return new Transitions(context, shellInit, shellController, organizer, pool,
                 displayController, mainExecutor, mainHandler, animExecutor);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index 195ff50..2fafe67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -48,7 +48,6 @@
         try {
             int result = Settings.System.getIntForUser(context.getContentResolver(),
                     Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
-            ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result);
             return result != 0;
         } catch (Exception e) {
             ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
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 c91d54a..b7749fc 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
@@ -69,22 +69,28 @@
 
     /**
      * Mark a task with given [taskId] as active.
+     *
+     * @return `true` if the task was not active
      */
-    fun addActiveTask(taskId: Int) {
+    fun addActiveTask(taskId: Int): Boolean {
         val added = activeTasks.add(taskId)
         if (added) {
             activeTasksListeners.onEach { it.onActiveTasksChanged() }
         }
+        return added
     }
 
     /**
      * Remove task with given [taskId] from active tasks.
+     *
+     * @return `true` if the task was active
      */
-    fun removeActiveTask(taskId: Int) {
+    fun removeActiveTask(taskId: Int): Boolean {
         val removed = activeTasks.remove(taskId)
         if (removed) {
             activeTasksListeners.onEach { it.onActiveTasksChanged() }
         }
+        return removed
     }
 
     /**
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 eaa7158..90b35a5 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
@@ -87,11 +87,13 @@
         }
 
         if (DesktopModeStatus.IS_SUPPORTED && taskInfo.isVisible) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                    "Adding active freeform task: #%d", taskInfo.taskId);
-            mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTask(taskInfo.taskId));
-            mDesktopModeTaskRepository.ifPresent(
-                    it -> it.updateVisibleFreeformTasks(taskInfo.taskId, true));
+            mDesktopModeTaskRepository.ifPresent(repository -> {
+                if (repository.addActiveTask(taskInfo.taskId)) {
+                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+                            "Adding active freeform task: #%d", taskInfo.taskId);
+                }
+                repository.updateVisibleFreeformTasks(taskInfo.taskId, true);
+            });
         }
     }
 
@@ -102,11 +104,13 @@
         mTasks.remove(taskInfo.taskId);
 
         if (DesktopModeStatus.IS_SUPPORTED) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                    "Removing active freeform task: #%d", taskInfo.taskId);
-            mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId));
-            mDesktopModeTaskRepository.ifPresent(
-                    it -> it.updateVisibleFreeformTasks(taskInfo.taskId, false));
+            mDesktopModeTaskRepository.ifPresent(repository -> {
+                if (repository.removeActiveTask(taskInfo.taskId)) {
+                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+                            "Removing active freeform task: #%d", taskInfo.taskId);
+                }
+                repository.updateVisibleFreeformTasks(taskInfo.taskId, false);
+            });
         }
 
         if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -123,13 +127,15 @@
         mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo);
 
         if (DesktopModeStatus.IS_SUPPORTED) {
-            if (taskInfo.isVisible) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                        "Adding active freeform task: #%d", taskInfo.taskId);
-                mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTask(taskInfo.taskId));
-            }
-            mDesktopModeTaskRepository.ifPresent(
-                    it -> it.updateVisibleFreeformTasks(taskInfo.taskId, taskInfo.isVisible));
+            mDesktopModeTaskRepository.ifPresent(repository -> {
+                if (taskInfo.isVisible) {
+                    if (repository.addActiveTask(taskInfo.taskId)) {
+                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+                                "Adding active freeform task: #%d", taskInfo.taskId);
+                    }
+                }
+                repository.updateVisibleFreeformTasks(taskInfo.taskId, taskInfo.isVisible);
+            });
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index b9746e3..cbed4b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -115,8 +115,8 @@
         // coordinates so offset the bounds to 0,0
         mTmpDestinationRect.offsetTo(0, 0);
         mTmpDestinationRect.inset(insets);
-        // Scale by the shortest edge and offset such that the top/left of the scaled inset source
-        // rect aligns with the top/left of the destination bounds
+        // Scale to the bounds no smaller than the destination and offset such that the top/left
+        // of the scaled inset source rect aligns with the top/left of the destination bounds
         final float scale;
         if (isInPipDirection
                 && sourceRectHint != null && sourceRectHint.width() < sourceBounds.width()) {
@@ -129,9 +129,8 @@
                     : (float) destinationBounds.height() / sourceBounds.height();
             scale = (1 - fraction) * startScale + fraction * endScale;
         } else {
-            scale = sourceBounds.width() <= sourceBounds.height()
-                    ? (float) destinationBounds.width() / sourceBounds.width()
-                    : (float) destinationBounds.height() / sourceBounds.height();
+            scale = Math.max((float) destinationBounds.width() / sourceBounds.width(),
+                    (float) destinationBounds.height() / sourceBounds.height());
         }
         final float left = destinationBounds.left - insets.left * scale;
         final float top = destinationBounds.top - insets.top * scale;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 2b36b4c..85bad17 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
@@ -335,6 +335,7 @@
         final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo();
         if (taskInfo != null) {
             startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
+                    mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
                     new Rect(mExitDestinationBounds), Surface.ROTATION_0);
         }
         mExitDestinationBounds.setEmpty();
@@ -475,6 +476,20 @@
                     taskInfo);
             return;
         }
+
+        // When exiting PiP, the PiP leash may be an Activity of a multi-windowing Task, for which
+        // case it may not be in the screen coordinate.
+        // Reparent the pip leash to the root with max layer so that we can animate it outside of
+        // parent crop, and make sure it is not covered by other windows.
+        final SurfaceControl pipLeash = pipChange.getLeash();
+        startTransaction.reparent(pipLeash, info.getRootLeash());
+        startTransaction.setLayer(pipLeash, Integer.MAX_VALUE);
+        // Note: because of this, the bounds to animate should be translated to the root coordinate.
+        final Point offset = info.getRootOffset();
+        final Rect currentBounds = mPipBoundsState.getBounds();
+        currentBounds.offset(-offset.x, -offset.y);
+        startTransaction.setPosition(pipLeash, currentBounds.left, currentBounds.top);
+
         mFinishCallback = (wct, wctCB) -> {
             mPipOrganizer.onExitPipFinished(taskInfo);
             finishCallback.onTransitionFinished(wct, wctCB);
@@ -496,18 +511,17 @@
             if (displayRotationChange != null) {
                 // Exiting PIP to fullscreen with orientation change.
                 startExpandAndRotationAnimation(info, startTransaction, finishTransaction,
-                        displayRotationChange, taskInfo, pipChange);
+                        displayRotationChange, taskInfo, pipChange, offset);
                 return;
             }
         }
 
         // Set the initial frame as scaling the end to the start.
         final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds());
-        final Point offset = pipChange.getEndRelOffset();
         destinationBounds.offset(-offset.x, -offset.y);
-        startTransaction.setWindowCrop(pipChange.getLeash(), destinationBounds);
-        mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(),
-                destinationBounds, mPipBoundsState.getBounds());
+        startTransaction.setWindowCrop(pipLeash, destinationBounds);
+        mSurfaceTransactionHelper.scale(startTransaction, pipLeash, destinationBounds,
+                currentBounds);
         startTransaction.apply();
 
         // Check if it is fixed rotation.
@@ -532,19 +546,21 @@
                 y = destinationBounds.bottom;
             }
             mSurfaceTransactionHelper.rotateAndScaleWithCrop(finishTransaction,
-                    pipChange.getLeash(), endBounds, endBounds, new Rect(), degree, x, y,
+                    pipLeash, endBounds, endBounds, new Rect(), degree, x, y,
                     true /* isExpanding */, rotationDelta == ROTATION_270 /* clockwise */);
         } else {
             rotationDelta = Surface.ROTATION_0;
         }
-        startExpandAnimation(taskInfo, pipChange.getLeash(), destinationBounds, rotationDelta);
+        startExpandAnimation(taskInfo, pipLeash, currentBounds, currentBounds, destinationBounds,
+                rotationDelta);
     }
 
     private void startExpandAndRotationAnimation(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull TransitionInfo.Change displayRotationChange,
-            @NonNull TaskInfo taskInfo, @NonNull TransitionInfo.Change pipChange) {
+            @NonNull TaskInfo taskInfo, @NonNull TransitionInfo.Change pipChange,
+            @NonNull Point offset) {
         final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(),
                 displayRotationChange.getEndRotation());
 
@@ -556,7 +572,6 @@
         final Rect startBounds = new Rect(pipChange.getStartAbsBounds());
         rotateBounds(startBounds, displayRotationChange.getStartAbsBounds(), rotateDelta);
         final Rect endBounds = new Rect(pipChange.getEndAbsBounds());
-        final Point offset = pipChange.getEndRelOffset();
         startBounds.offset(-offset.x, -offset.y);
         endBounds.offset(-offset.x, -offset.y);
 
@@ -592,11 +607,12 @@
     }
 
     private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
-            final Rect destinationBounds, final int rotationDelta) {
+            final Rect baseBounds, final Rect startBounds, final Rect endBounds,
+            final int rotationDelta) {
         final PipAnimationController.PipTransitionAnimator animator =
-                mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(),
-                        mPipBoundsState.getBounds(), destinationBounds, null,
-                        TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, rotationDelta);
+                mPipAnimationController.getAnimator(taskInfo, leash, baseBounds, startBounds,
+                        endBounds, null /* sourceHintRect */, TRANSITION_DIRECTION_LEAVE_PIP,
+                        0 /* startingAngle */, rotationDelta);
         animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration)
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 d7ca791..21a1310 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
@@ -108,6 +108,14 @@
     private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
             @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
+        final TransitSession pendingTransition = getPendingTransition(transition);
+        if (pendingTransition != null && pendingTransition.mCanceled) {
+            // The pending transition was canceled, so skip playing animation.
+            t.apply();
+            onFinish(null /* wct */, null /* wctCB */);
+            return;
+        }
+
         // Play some place-holder fade animations
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
@@ -170,9 +178,7 @@
     }
 
     boolean isPendingTransition(IBinder transition) {
-        return isPendingEnter(transition)
-                || isPendingDismiss(transition)
-                || isPendingRecent(transition);
+        return getPendingTransition(transition) != null;
     }
 
     boolean isPendingEnter(IBinder transition) {
@@ -187,22 +193,38 @@
         return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
     }
 
+    @Nullable
+    private TransitSession getPendingTransition(IBinder transition) {
+        if (isPendingEnter(transition)) {
+            return mPendingEnter;
+        } else if (isPendingRecent(transition)) {
+            return mPendingRecent;
+        } else if (isPendingDismiss(transition)) {
+            return mPendingDismiss;
+        }
+
+        return null;
+    }
+
     /** Starts a transition to enter split with a remote transition animator. */
     IBinder startEnterTransition(
             @WindowManager.TransitionType int transitType,
             WindowContainerTransaction wct,
             @Nullable RemoteTransition remoteTransition,
             Transitions.TransitionHandler handler,
-            @Nullable TransitionCallback callback) {
+            @Nullable TransitionConsumedCallback consumedCallback,
+            @Nullable TransitionFinishedCallback finishedCallback) {
         final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
-        setEnterTransition(transition, remoteTransition, callback);
+        setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback);
         return transition;
     }
 
     /** Sets a transition to enter split. */
     void setEnterTransition(@NonNull IBinder transition,
-            @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) {
-        mPendingEnter = new TransitSession(transition, callback);
+            @Nullable RemoteTransition remoteTransition,
+            @Nullable TransitionConsumedCallback consumedCallback,
+            @Nullable TransitionFinishedCallback finishedCallback) {
+        mPendingEnter = new TransitSession(transition, consumedCallback, finishedCallback);
 
         if (remoteTransition != null) {
             // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
@@ -237,8 +259,9 @@
     }
 
     void setRecentTransition(@NonNull IBinder transition,
-            @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) {
-        mPendingRecent = new TransitSession(transition, callback);
+            @Nullable RemoteTransition remoteTransition,
+            @Nullable TransitionFinishedCallback finishCallback) {
+        mPendingRecent = new TransitSession(transition, null /* consumedCb */, finishCallback);
 
         if (remoteTransition != null) {
             // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
@@ -248,7 +271,7 @@
         }
 
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
-                        + " deduced Enter recent panel");
+                + " deduced Enter recent panel");
     }
 
     void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
@@ -256,14 +279,9 @@
         if (mergeTarget != mAnimatingTransition) return;
 
         if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) {
-            mPendingRecent.mCallback = new TransitionCallback() {
-                @Override
-                public void onTransitionFinished(WindowContainerTransaction finishWct,
-                        SurfaceControl.Transaction finishT) {
-                    // Since there's an entering transition merged, recent transition no longer
-                    // need to handle entering split screen after the transition finished.
-                }
-            };
+            // Since there's an entering transition merged, recent transition no longer
+            // need to handle entering split screen after the transition finished.
+            mPendingRecent.setFinishedCallback(null);
         }
 
         if (mActiveRemoteHandler != null) {
@@ -277,7 +295,7 @@
     }
 
     boolean end() {
-        // If its remote, there's nothing we can do right now.
+        // If It's remote, there's nothing we can do right now.
         if (mActiveRemoteHandler != null) return false;
         for (int i = mAnimations.size() - 1; i >= 0; --i) {
             final Animator anim = mAnimations.get(i);
@@ -290,20 +308,20 @@
             @Nullable SurfaceControl.Transaction finishT) {
         if (isPendingEnter(transition)) {
             if (!aborted) {
-                // An enter transition got merged, appends the rest operations to finish entering
+                // An entering transition got merged, appends the rest operations to finish entering
                 // split screen.
                 mStageCoordinator.finishEnterSplitScreen(finishT);
                 mPendingRemoteHandler = null;
             }
 
-            mPendingEnter.mCallback.onTransitionConsumed(aborted);
+            mPendingEnter.onConsumed(aborted);
             mPendingEnter = null;
             mPendingRemoteHandler = null;
         } else if (isPendingDismiss(transition)) {
-            mPendingDismiss.mCallback.onTransitionConsumed(aborted);
+            mPendingDismiss.onConsumed(aborted);
             mPendingDismiss = null;
         } else if (isPendingRecent(transition)) {
-            mPendingRecent.mCallback.onTransitionConsumed(aborted);
+            mPendingRecent.onConsumed(aborted);
             mPendingRecent = null;
             mPendingRemoteHandler = null;
         }
@@ -312,23 +330,16 @@
     void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
         if (!mAnimations.isEmpty()) return;
 
-        TransitionCallback callback = null;
+        if (wct == null) wct = new WindowContainerTransaction();
         if (isPendingEnter(mAnimatingTransition)) {
-            callback = mPendingEnter.mCallback;
+            mPendingEnter.onFinished(wct, mFinishTransaction);
             mPendingEnter = null;
-        }
-        if (isPendingDismiss(mAnimatingTransition)) {
-            callback = mPendingDismiss.mCallback;
-            mPendingDismiss = null;
-        }
-        if (isPendingRecent(mAnimatingTransition)) {
-            callback = mPendingRecent.mCallback;
+        } else if (isPendingRecent(mAnimatingTransition)) {
+            mPendingRecent.onFinished(wct, mFinishTransaction);
             mPendingRecent = null;
-        }
-
-        if (callback != null) {
-            if (wct == null) wct = new WindowContainerTransaction();
-            callback.onTransitionFinished(wct, mFinishTransaction);
+        } else if (isPendingDismiss(mAnimatingTransition)) {
+            mPendingDismiss.onFinished(wct, mFinishTransaction);
+            mPendingDismiss = null;
         }
 
         mPendingRemoteHandler = null;
@@ -363,10 +374,7 @@
                 onFinish(null /* wct */, null /* wctCB */);
             });
         };
-        va.addListener(new Animator.AnimatorListener() {
-            @Override
-            public void onAnimationStart(Animator animation) { }
-
+        va.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 finisher.run();
@@ -376,9 +384,6 @@
             public void onAnimationCancel(Animator animation) {
                 finisher.run();
             }
-
-            @Override
-            public void onAnimationRepeat(Animator animation) { }
         });
         mAnimations.add(va);
         mTransitions.getAnimExecutor().execute(va::start);
@@ -432,24 +437,66 @@
                 || info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
     }
 
-    /** Clean-up callbacks for transition. */
-    interface TransitionCallback {
-        /** Calls when the transition got consumed. */
-        default void onTransitionConsumed(boolean aborted) {}
+    /** Calls when the transition got consumed. */
+    interface TransitionConsumedCallback {
+        void onConsumed(boolean aborted);
+    }
 
-        /** Calls when the transition finished. */
-        default void onTransitionFinished(WindowContainerTransaction finishWct,
-                SurfaceControl.Transaction finishT) {}
+    /** Calls when the transition finished. */
+    interface TransitionFinishedCallback {
+        void onFinished(WindowContainerTransaction wct, SurfaceControl.Transaction t);
     }
 
     /** Session for a transition and its clean-up callback. */
     static class TransitSession {
         final IBinder mTransition;
-        TransitionCallback mCallback;
+        TransitionConsumedCallback mConsumedCallback;
+        TransitionFinishedCallback mFinishedCallback;
 
-        TransitSession(IBinder transition, @Nullable TransitionCallback callback) {
+        /** Whether the transition was canceled. */
+        boolean mCanceled;
+
+        TransitSession(IBinder transition,
+                @Nullable TransitionConsumedCallback consumedCallback,
+                @Nullable TransitionFinishedCallback finishedCallback) {
             mTransition = transition;
-            mCallback = callback != null ? callback : new TransitionCallback() {};
+            mConsumedCallback = consumedCallback;
+            mFinishedCallback = finishedCallback;
+
+        }
+
+        /** Sets transition consumed callback. */
+        void setConsumedCallback(@Nullable TransitionConsumedCallback callback) {
+            mConsumedCallback = callback;
+        }
+
+        /** Sets transition finished callback. */
+        void setFinishedCallback(@Nullable TransitionFinishedCallback callback) {
+            mFinishedCallback = callback;
+        }
+
+        /**
+         * Cancels the transition. This should be called before playing animation. A canceled
+         * transition will skip playing animation.
+         *
+         * @param finishedCb new finish callback to override.
+         */
+        void cancel(@Nullable TransitionFinishedCallback finishedCb) {
+            mCanceled = true;
+            setFinishedCallback(finishedCb);
+        }
+
+        void onConsumed(boolean aborted) {
+            if (mConsumedCallback != null) {
+                mConsumedCallback.onConsumed(aborted);
+            }
+        }
+
+        void onFinished(WindowContainerTransaction finishWct,
+                SurfaceControl.Transaction finishT) {
+            if (mFinishedCallback != null) {
+                mFinishedCallback.onFinished(finishWct, finishT);
+            }
         }
     }
 
@@ -459,7 +506,7 @@
         final @SplitScreen.StageType int mDismissTop;
 
         DismissTransition(IBinder transition, int reason, int dismissTop) {
-            super(transition, null /* callback */);
+            super(transition, null /* consumedCallback */, null /* finishedCallback */);
             this.mReason = reason;
             this.mDismissTop = dismissTop;
         }
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 e2ac01f..15a1133 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
@@ -115,6 +115,7 @@
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
@@ -226,33 +227,36 @@
                 }
             };
 
-    private final SplitScreenTransitions.TransitionCallback mRecentTransitionCallback =
-            new SplitScreenTransitions.TransitionCallback() {
-        @Override
-        public void onTransitionFinished(WindowContainerTransaction finishWct,
-                SurfaceControl.Transaction finishT) {
-            // Check if the recent transition is finished by returning to the current split, so we
-            // can restore the divider bar.
-            for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
-                final WindowContainerTransaction.HierarchyOp op =
-                        finishWct.getHierarchyOps().get(i);
-                final IBinder container = op.getContainer();
-                if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
-                        && (mMainStage.containsContainer(container)
-                        || mSideStage.containsContainer(container))) {
-                    updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */);
-                    setDividerVisibility(true, finishT);
-                    return;
-                }
-            }
+    private final SplitScreenTransitions.TransitionFinishedCallback
+            mRecentTransitionFinishedCallback =
+            new SplitScreenTransitions.TransitionFinishedCallback() {
+                @Override
+                public void onFinished(WindowContainerTransaction finishWct,
+                        SurfaceControl.Transaction finishT) {
+                    // Check if the recent transition is finished by returning to the current
+                    // split, so we
+                    // can restore the divider bar.
+                    for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
+                        final WindowContainerTransaction.HierarchyOp op =
+                                finishWct.getHierarchyOps().get(i);
+                        final IBinder container = op.getContainer();
+                        if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
+                                && (mMainStage.containsContainer(container)
+                                || mSideStage.containsContainer(container))) {
+                            updateSurfaceBounds(mSplitLayout, finishT,
+                                    false /* applyResizingOffset */);
+                            setDividerVisibility(true, finishT);
+                            return;
+                        }
+                    }
 
-            // Dismiss the split screen if it's not returning to split.
-            prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
-            setSplitsVisible(false);
-            setDividerVisibility(false, finishT);
-            logExit(EXIT_REASON_UNKNOWN);
-        }
-    };
+                    // Dismiss the split screen if it's not returning to split.
+                    prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
+                    setSplitsVisible(false);
+                    setDividerVisibility(false, finishT);
+                    logExit(EXIT_REASON_UNKNOWN);
+                }
+            };
 
     protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
             ShellTaskOrganizer taskOrganizer, DisplayController displayController,
@@ -389,15 +393,11 @@
         if (ENABLE_SHELL_TRANSITIONS) {
             prepareEnterSplitScreen(wct);
             mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct,
-                    null, this, new SplitScreenTransitions.TransitionCallback() {
-                        @Override
-                        public void onTransitionFinished(WindowContainerTransaction finishWct,
-                                SurfaceControl.Transaction finishT) {
-                            if (!evictWct.isEmpty()) {
-                                finishWct.merge(evictWct, true);
-                            }
+                    null, this, null /* consumedCallback */, (finishWct, finishT) -> {
+                        if (!evictWct.isEmpty()) {
+                            finishWct.merge(evictWct, true);
                         }
-                    });
+                    } /* finishedCallback */);
         } else {
             if (!evictWct.isEmpty()) {
                 wct.merge(evictWct, true /* transfer */);
@@ -434,28 +434,25 @@
 
         options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
         wct.sendPendingIntent(intent, fillInIntent, options);
+
+        // If split screen is not activated, we're expecting to open a pair of apps to split.
+        final int transitType = mMainStage.isActive()
+                ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
         prepareEnterSplitScreen(wct, null /* taskInfo */, position);
 
-        mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, null, this,
-                new SplitScreenTransitions.TransitionCallback() {
-                    @Override
-                    public void onTransitionConsumed(boolean aborted) {
-                        // Switch the split position if launching as MULTIPLE_TASK failed.
-                        if (aborted
-                                && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
-                            setSideStagePositionAnimated(
-                                    SplitLayout.reversePosition(mSideStagePosition));
-                        }
+        mSplitTransitions.startEnterTransition(transitType, wct, null, this,
+                aborted -> {
+                    // Switch the split position if launching as MULTIPLE_TASK failed.
+                    if (aborted && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+                        setSideStagePositionAnimated(
+                                SplitLayout.reversePosition(mSideStagePosition));
                     }
-
-                    @Override
-                    public void onTransitionFinished(WindowContainerTransaction finishWct,
-                            SurfaceControl.Transaction finishT) {
-                        if (!evictWct.isEmpty()) {
-                            finishWct.merge(evictWct, true);
-                        }
+                } /* consumedCallback */,
+                (finishWct, finishT) -> {
+                    if (!evictWct.isEmpty()) {
+                        finishWct.merge(evictWct, true);
                     }
-                });
+                } /* finishedCallback */);
     }
 
     /** Launches an activity into split by legacy transition. */
@@ -564,9 +561,9 @@
     /**
      * Starts with the second task to a split pair in one transition.
      *
-     * @param wct transaction to start the first task
+     * @param wct        transaction to start the first task
      * @param instanceId if {@code null}, will not log. Otherwise it will be used in
-     *      {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
+     *                   {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
      */
     private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
             @Nullable Bundle mainOptions, float splitRatio,
@@ -592,7 +589,7 @@
         wct.startTask(mainTaskId, mainOptions);
 
         mSplitTransitions.startEnterTransition(
-                TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null);
+                TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null, null);
         setEnterInstanceId(instanceId);
     }
 
@@ -639,7 +636,7 @@
     }
 
     /**
-     * @param wct transaction to start the first task
+     * @param wct        transaction to start the first task
      * @param instanceId if {@code null}, will not log. Otherwise it will be used in
      *                   {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
      */
@@ -850,15 +847,44 @@
     void setSideStagePositionAnimated(@SplitPosition int sideStagePosition) {
         if (mSideStagePosition == sideStagePosition) return;
         SurfaceControl.Transaction t = mTransactionPool.acquire();
+        mTempRect1.setEmpty();
         final StageTaskListener topLeftStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
+        final SurfaceControl topLeftScreenshot = ScreenshotUtils.takeScreenshot(t,
+                topLeftStage.mRootLeash, mTempRect1, Integer.MAX_VALUE - 1);
         final StageTaskListener bottomRightStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
+        final SurfaceControl bottomRightScreenshot = ScreenshotUtils.takeScreenshot(t,
+                bottomRightStage.mRootLeash, mTempRect1, Integer.MAX_VALUE - 1);
         mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash,
-                () -> {
-                    setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition),
-                            null /* wct */);
-                    mTransactionPool.release(t);
+                insets -> {
+                    WindowContainerTransaction wct = new WindowContainerTransaction();
+                    setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), wct);
+                    mSyncQueue.queue(wct);
+                    mSyncQueue.runInSync(st -> {
+                        updateSurfaceBounds(mSplitLayout, st, false /* applyResizingOffset */);
+                        st.setPosition(topLeftScreenshot, -insets.left, -insets.top);
+                        st.setPosition(bottomRightScreenshot, insets.left, insets.top);
+
+                        final ValueAnimator va = ValueAnimator.ofFloat(1, 0);
+                        va.addUpdateListener(valueAnimator-> {
+                            final float progress = (float) valueAnimator.getAnimatedValue();
+                            t.setAlpha(topLeftScreenshot, progress);
+                            t.setAlpha(bottomRightScreenshot, progress);
+                            t.apply();
+                        });
+                        va.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationEnd(
+                                    @androidx.annotation.NonNull Animator animation) {
+                                t.remove(topLeftScreenshot);
+                                t.remove(bottomRightScreenshot);
+                                t.apply();
+                                mTransactionPool.release(t);
+                            }
+                        });
+                        va.start();
+                    });
                 });
     }
 
@@ -1082,15 +1108,15 @@
         switch (exitReason) {
             // One of the apps doesn't support MW
             case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
-            // User has explicitly dragged the divider to dismiss split
+                // User has explicitly dragged the divider to dismiss split
             case EXIT_REASON_DRAG_DIVIDER:
-            // Either of the split apps have finished
+                // Either of the split apps have finished
             case EXIT_REASON_APP_FINISHED:
-            // One of the children enters PiP
+                // One of the children enters PiP
             case EXIT_REASON_CHILD_TASK_ENTER_PIP:
-            // One of the apps occludes lock screen.
+                // One of the apps occludes lock screen.
             case EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP:
-            // User has unlocked the device after folded
+                // User has unlocked the device after folded
             case EXIT_REASON_DEVICE_FOLDED:
                 return true;
             default:
@@ -1839,7 +1865,7 @@
                         || activityType == ACTIVITY_TYPE_RECENTS) {
                     // Enter overview panel, so start recent transition.
                     mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(),
-                            mRecentTransitionCallback);
+                            mRecentTransitionFinishedCallback);
                 } else if (mSplitTransitions.mPendingRecent == null) {
                     // If split-task is not controlled by recents animation
                     // and occluded by the other fullscreen task, dismiss both.
@@ -1853,8 +1879,8 @@
                 // One task is appearing into split, prepare to enter split screen.
                 out = new WindowContainerTransaction();
                 prepareEnterSplitScreen(out);
-                mSplitTransitions.setEnterTransition(
-                        transition, request.getRemoteTransition(), null /* callback */);
+                mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
+                        null /* consumedCallback */, null /* finishedCallback */);
             }
         }
         return out;
@@ -1873,7 +1899,7 @@
         }
         final @WindowManager.TransitionType int type = request.getType();
         if (isSplitActive() && !isOpeningType(type)
-                    && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
+                && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  One of the splits became "
                             + "empty during a mixed transition (one not handled by split),"
                             + " so make sure split-screen state is cleaned-up. "
@@ -2031,17 +2057,21 @@
             }
         }
 
-        // TODO(b/250853925): fallback logic. Probably start a new transition to exit split before
-        //       applying anything here. Ideally consolidate with transition-merging.
         if (info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
             if (mainChild == null && sideChild == null) {
-                throw new IllegalStateException("Launched a task in split, but didn't receive any"
-                        + " task in transition.");
+                Log.w(TAG, "Launched a task in split, but didn't receive any task in transition.");
+                mSplitTransitions.mPendingEnter.cancel(null /* finishedCb */);
+                return true;
             }
         } else {
             if (mainChild == null || sideChild == null) {
-                throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
+                Log.w(TAG, "Launched 2 tasks in split, but didn't receive"
                         + " 2 tasks in transition. Possibly one of them failed to launch");
+                final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN :
+                        (sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED);
+                mSplitTransitions.mPendingEnter.cancel(
+                        (cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct));
+                return true;
             }
         }
 
@@ -2305,7 +2335,7 @@
                 final int stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
                 final WindowContainerTransaction wct = new WindowContainerTransaction();
                 prepareExitSplitScreen(stageType, wct);
-                mSplitTransitions.startDismissTransition(wct,StageCoordinator.this, stageType,
+                mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType,
                         EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
                 mSplitUnsupportedToast.show();
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index af79386..928e71f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -46,6 +46,7 @@
 import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
 import static android.window.TransitionInfo.FLAG_FILLS_TASK;
 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -319,6 +320,17 @@
         final int wallpaperTransit = getWallpaperTransitType(info);
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change.hasAllFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY
+                    | FLAG_IS_BEHIND_STARTING_WINDOW)) {
+                // Don't animate embedded activity if it is covered by the starting window.
+                // Non-embedded case still needs animation because the container can still animate
+                // the starting window together, e.g. CLOSE or CHANGE type.
+                continue;
+            }
+            if (change.hasFlags(TransitionInfo.FLAGS_IS_NON_APP_WINDOW)) {
+                // Wallpaper, IME, and system windows don't need any default animations.
+                continue;
+            }
             final boolean isTask = change.getTaskInfo() != null;
             boolean isSeamlessDisplayChange = false;
 
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 394d6f6..89205a6 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
@@ -122,6 +122,8 @@
     private final ShellController mShellController;
     private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
 
+    private boolean mIsRegistered = false;
+
     /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
     private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
 
@@ -163,19 +165,18 @@
                 displayController, pool, mainExecutor, mainHandler, animExecutor);
         mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor);
         mShellController = shellController;
-        shellInit.addInitCallback(this::onInit, this);
-    }
-
-    private void onInit() {
-        mShellController.addExternalInterface(KEY_EXTRA_SHELL_SHELL_TRANSITIONS,
-                this::createExternalInterface, this);
-
         // The very last handler (0 in the list) should be the default one.
         mHandlers.add(mDefaultTransitionHandler);
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
         // Next lowest priority is remote transitions.
         mHandlers.add(mRemoteTransitionHandler);
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
+        shellInit.addInitCallback(this::onInit, this);
+    }
+
+    private void onInit() {
+        mShellController.addExternalInterface(KEY_EXTRA_SHELL_SHELL_TRANSITIONS,
+                this::createExternalInterface, this);
 
         ContentResolver resolver = mContext.getContentResolver();
         mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
@@ -186,13 +187,23 @@
                 new SettingsObserver());
 
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            mIsRegistered = true;
             // Register this transition handler with Core
-            mOrganizer.registerTransitionPlayer(mPlayerImpl);
+            try {
+                mOrganizer.registerTransitionPlayer(mPlayerImpl);
+            } catch (RuntimeException e) {
+                mIsRegistered = false;
+                throw e;
+            }
             // Pre-load the instance.
             TransitionMetrics.getInstance();
         }
     }
 
+    public boolean isRegistered() {
+        return mIsRegistered;
+    }
+
     private float getTransitionAnimationScaleSetting() {
         return fixScale(Settings.Global.getFloat(mContext.getContentResolver(),
                 Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat(
@@ -330,6 +341,15 @@
             final SurfaceControl leash = change.getLeash();
             final int mode = info.getChanges().get(i).getMode();
 
+            if (mode == TRANSIT_TO_FRONT
+                    && ((change.getStartAbsBounds().height() != change.getEndAbsBounds().height()
+                    || change.getStartAbsBounds().width() != change.getEndAbsBounds().width()))) {
+                // When the window is moved to front with a different size, make sure the crop is
+                // updated to prevent it from using the old crop.
+                t.setWindowCrop(leash, change.getEndAbsBounds().width(),
+                        change.getEndAbsBounds().height());
+            }
+
             // Don't move anything that isn't independent within its parents
             if (!TransitionInfo.isIndependent(change, info)) {
                 if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index 73159c9..ad7a531 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -145,15 +145,19 @@
         // robust enough to get the correct end state.
     }
 
+    @Presubmit
     @Test
     fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
 
+    @Presubmit
     @Test
     fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
 
+    @Presubmit
     @Test
     fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
 
+    @Presubmit
     @Test
     fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
         primaryApp,
@@ -161,6 +165,7 @@
         portraitPosTop = true
     )
 
+    @Presubmit
     @Test
     fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
         secondaryApp,
@@ -168,9 +173,11 @@
         portraitPosTop = false
     )
 
+    @Presubmit
     @Test
     fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
 
+    @Presubmit
     @Test
     fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(secondaryApp)
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 55883ab..d01f3d3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -39,6 +39,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -110,6 +111,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void instantiateController_registerDumpCallback() {
         doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
         when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
@@ -118,6 +120,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void instantiateController_registerCommandCallback() {
         doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
         when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
@@ -126,6 +129,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testControllerRegistersKeyguardChangeListener() {
         doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
         when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
@@ -134,6 +138,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void instantiateController_addExternalInterface() {
         doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
         when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
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 ea0033b..652f9b3 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
@@ -181,7 +181,7 @@
 
         IBinder transition = mSplitScreenTransitions.startEnterTransition(
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
-                new RemoteTransition(testRemote), mStageCoordinator, null);
+                new RemoteTransition(testRemote), mStageCoordinator, null, null);
         mMainStage.onTaskAppeared(mMainChild, createMockSurface());
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -421,7 +421,7 @@
         TransitionInfo enterInfo = createEnterPairInfo();
         IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
-                new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null);
+                new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null, null);
         mMainStage.onTaskAppeared(mMainChild, createMockSurface());
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 8350870..3569860 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -50,6 +50,7 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -113,6 +114,7 @@
     private StageCoordinator mStageCoordinator;
 
     @Before
+    @UiThreadTest
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 0513447..35258a3 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -20,6 +20,7 @@
 #include <android/api-level.h>
 #else
 #define __ANDROID_API_P__ 28
+#define __ANDROID_API_U__ 34
 #endif
 #include <androidfw/ResourceTypes.h>
 #include <hwui/Canvas.h>
@@ -30,8 +31,9 @@
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedStringChars.h>
 
-#include "FontUtils.h"
 #include "Bitmap.h"
+#include "FontUtils.h"
+#include "SkAndroidFrameworkUtils.h"
 #include "SkBitmap.h"
 #include "SkBlendMode.h"
 #include "SkClipOp.h"
@@ -42,10 +44,10 @@
 #include "SkMatrix.h"
 #include "SkPath.h"
 #include "SkPoint.h"
+#include "SkRRect.h"
 #include "SkRect.h"
 #include "SkRefCnt.h"
 #include "SkRegion.h"
-#include "SkRRect.h"
 #include "SkScalar.h"
 #include "SkVertices.h"
 
@@ -710,6 +712,9 @@
 
 static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) {
     Canvas::setCompatibilityVersion(apiLevel);
+    if (apiLevel < __ANDROID_API_U__) {
+        SkAndroidFrameworkUtils::UseLegacyLocalMatrixConcatenation();
+    }
 }
 
 static void punchHole(JNIEnv* env, jobject, jlong canvasPtr, jfloat left, jfloat top, jfloat right,
@@ -800,7 +805,6 @@
     ret |= RegisterMethodsOrDie(env, "android/graphics/BaseCanvas", gDrawMethods, NELEM(gDrawMethods));
     ret |= RegisterMethodsOrDie(env, "android/graphics/BaseRecordingCanvas", gDrawMethods, NELEM(gDrawMethods));
     return ret;
-
 }
 
 }; // namespace android
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 6a0c5a8..d09bc47 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -472,11 +472,11 @@
     mRenderThread.pushBackFrameCallback(this);
 }
 
-nsecs_t CanvasContext::draw() {
+std::optional<nsecs_t> CanvasContext::draw() {
     if (auto grContext = getGrContext()) {
         if (grContext->abandoned()) {
             LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
-            return 0;
+            return std::nullopt;
         }
     }
     SkRect dirty;
@@ -498,7 +498,7 @@
             std::invoke(func, false /* didProduceBuffer */);
         }
         mFrameCommitCallbacks.clear();
-        return 0;
+        return std::nullopt;
     }
 
     ScopedActiveContext activeContext(this);
@@ -543,6 +543,8 @@
     }
 
     bool requireSwap = false;
+    bool didDraw = false;
+
     int error = OK;
     bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult.success, windowDirty,
                                                 mCurrentFrameInfo, &requireSwap);
@@ -553,7 +555,7 @@
     mIsDirty = false;
 
     if (requireSwap) {
-        bool didDraw = true;
+        didDraw = true;
         // Handle any swapchain errors
         error = mNativeSurface->getAndClearError();
         if (error == TIMED_OUT) {
@@ -649,7 +651,9 @@
     }
 
     mRenderThread.cacheManager().onFrameCompleted();
-    return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
+    return didDraw ? std::make_optional(
+                             mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration))
+                   : std::nullopt;
 }
 
 void CanvasContext::reportMetricsWithPresentTime() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 748ab96..db96cfb 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -138,7 +138,7 @@
     bool makeCurrent();
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
     // Returns the DequeueBufferDuration.
-    nsecs_t draw();
+    std::optional<nsecs_t> draw();
     void destroy();
 
     // IFrameCallback, Choreographer-driven frame callback entry point
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 03f02de..dc7676c 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -19,6 +19,7 @@
 #include <dlfcn.h>
 #include <gui/TraceUtils.h>
 #include <utils/Log.h>
+
 #include <algorithm>
 
 #include "../DeferredLayerUpdater.h"
@@ -28,6 +29,7 @@
 #include "CanvasContext.h"
 #include "RenderThread.h"
 #include "thread/CommonPool.h"
+#include "utils/TimeUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -146,6 +148,7 @@
 
     bool canUnblockUiThread;
     bool canDrawThisFrame;
+    bool didDraw = false;
     {
         TreeInfo info(TreeInfo::MODE_FULL, *mContext);
         info.forceDrawFrame = mForceDrawFrame;
@@ -188,7 +191,9 @@
 
     nsecs_t dequeueBufferDuration = 0;
     if (CC_LIKELY(canDrawThisFrame)) {
-        dequeueBufferDuration = context->draw();
+        std::optional<nsecs_t> drawResult = context->draw();
+        didDraw = drawResult.has_value();
+        dequeueBufferDuration = drawResult.value_or(0);
     } else {
         // Do a flush in case syncFrameState performed any texture uploads. Since we skipped
         // the draw() call, those uploads (or deletes) will end up sitting in the queue.
@@ -209,8 +214,9 @@
     }
 
     if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
-    constexpr int64_t kSanityCheckLowerBound = 100000;       // 0.1ms
-    constexpr int64_t kSanityCheckUpperBound = 10000000000;  // 10s
+
+    constexpr int64_t kSanityCheckLowerBound = 100_us;
+    constexpr int64_t kSanityCheckUpperBound = 10_s;
     int64_t targetWorkDuration = frameDeadline - intendedVsync;
     targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
     if (targetWorkDuration > kSanityCheckLowerBound &&
@@ -219,12 +225,15 @@
         mLastTargetWorkDuration = targetWorkDuration;
         mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
     }
-    int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
-    int64_t actualDuration = frameDuration -
-                             (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
-                             dequeueBufferDuration;
-    if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
-        mHintSessionWrapper->reportActualWorkDuration(actualDuration);
+
+    if (didDraw) {
+        int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
+        int64_t actualDuration = frameDuration -
+                                 (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+                                 dequeueBufferDuration;
+        if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
+            mHintSessionWrapper->reportActualWorkDuration(actualDuration);
+        }
     }
 
     mLastDequeueBufferDuration = dequeueBufferDuration;
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index 7a412a0..b38f9ea 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -207,7 +207,7 @@
     /**
      * Returns {@code true} if GNSS chipset supports single shot locating, {@code false} otherwise.
      */
-    public boolean hasSingleShot() {
+    public boolean hasSingleShotFix() {
         return (mTopFlags & TOP_HAL_CAPABILITY_SINGLE_SHOT) != 0;
     }
 
@@ -482,7 +482,7 @@
         if (hasMsa()) {
             builder.append("MSA ");
         }
-        if (hasSingleShot()) {
+        if (hasSingleShotFix()) {
             builder.append("SINGLE_SHOT ");
         }
         if (hasOnDemandTime()) {
@@ -602,7 +602,7 @@
         /**
          * Sets single shot locating capability.
          */
-        public @NonNull Builder setHasSingleShot(boolean capable) {
+        public @NonNull Builder setHasSingleShotFix(boolean capable) {
             mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_SINGLE_SHOT, capable);
             return this;
         }
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 23390fc..09f40e8 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -199,15 +199,15 @@
      * <li>93-106 as the frequency channel number (FCN) (-7 to +6) plus 100.
      * i.e. encode FCN of -7 as 93, 0 as 100, and +6 as 106</li>
      * </ul></li>
-     * <li>QZSS: 193-200</li>
+     * <li>QZSS: 183-206</li>
      * <li>Galileo: 1-36</li>
-     * <li>Beidou: 1-37</li>
+     * <li>Beidou: 1-63</li>
      * <li>IRNSS: 1-14</li>
      * </ul>
      *
      * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
      */
-    @IntRange(from = 1, to = 200)
+    @IntRange(from = 1, to = 206)
     public int getSvid(@IntRange(from = 0) int satelliteIndex) {
         return mSvidWithFlags[satelliteIndex] >> SVID_SHIFT_WIDTH;
     }
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 819358b..60e3a306 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -569,6 +569,17 @@
         }
     }
 
+    private boolean isMuteAffectingActiveState() {
+        if (mMutedState == PLAYER_MUTE_INVALID) {
+            // mute state is not set, therefore it will not affect the active state
+            return false;
+        }
+
+        return (mMutedState & PLAYER_MUTE_CLIENT_VOLUME) != 0
+                || (mMutedState & PLAYER_MUTE_VOLUME_SHAPER) != 0
+                || (mMutedState & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0;
+    }
+
     /**
      * @hide
      * Returns true if the player is considered "active", i.e. actively playing with unmuted
@@ -580,8 +591,7 @@
     public boolean isActive() {
         switch (mPlayerState) {
             case PLAYER_STATE_STARTED:
-                return mMutedState == 0
-                        || mMutedState == PLAYER_MUTE_INVALID;  // only send true if not muted
+                return !isMuteAffectingActiveState();
             case PLAYER_STATE_UNKNOWN:
             case PLAYER_STATE_RELEASED:
             case PLAYER_STATE_IDLE:
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 26cb9f8..b4b908d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -104,8 +104,8 @@
     private final String mPackageName;
 
     /**
-     * Stores the latest copy of all routes received from {@link MediaRouter2ServiceImpl}, without
-     * any filtering, sorting, or deduplication.
+     * Stores the latest copy of all routes received from the system server, without any filtering,
+     * sorting, or deduplication.
      *
      * <p>Uses {@link MediaRoute2Info#getId()} to set each entry's key.
      */
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreActivity.java
index 24cb5f9..602e31c 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreActivity.java
@@ -85,7 +85,7 @@
 
         SliceStoreBroadcastReceiver.updateSliceStoreActivity(mCapability, this);
 
-        mWebView = new WebView(getApplicationContext());
+        mWebView = new WebView(this);
         setContentView(mWebView);
         mWebView.loadUrl(mUrl.toString());
         // TODO(b/245882601): Get back response from WebView
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index 428f2dc..2000d96 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -49,7 +49,6 @@
     <style name="DescriptionSummary">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
-        <item name="android:gravity">center</item>
         <item name="android:layout_marginTop">18dp</item>
         <item name="android:layout_marginLeft">18dp</item>
         <item name="android:layout_marginRight">18dp</item>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 56fb1a9..0988cba 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -36,7 +36,7 @@
 import com.android.credentialmanager.createflow.RequestDisplayInfo
 import com.android.credentialmanager.getflow.GetCredentialUiState
 import com.android.credentialmanager.getflow.GetScreenState
-import com.android.credentialmanager.jetpack.CredentialEntryUi.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
+import com.android.credentialmanager.jetpack.provider.CredentialEntryUi.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
 
 // Consider repo per screen, similar to view model?
 class CredentialManagerRepo(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index e037db7..2ba8748 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -35,7 +35,7 @@
         ProviderInfo(
           // TODO: replace to extract from the service data structure when available
           icon = context.getDrawable(R.drawable.ic_passkey)!!,
-          name = it.providerId,
+          name = it.providerFlattenedComponentName,
           displayName = it.providerDisplayName,
           credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
           credentialOptions = toCredentialOptionInfoList(it.credentialEntries, context)
@@ -79,7 +79,7 @@
         com.android.credentialmanager.createflow.ProviderInfo(
           // TODO: replace to extract from the service data structure when available
           icon = context.getDrawable(R.drawable.ic_passkey)!!,
-          name = it.providerId,
+          name = it.providerFlattenedComponentName,
           displayName = it.providerDisplayName,
           credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
           createOptions = toCreationOptionInfoList(it.credentialEntries, context),
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
index 8e30208..aeea46a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
@@ -39,8 +39,8 @@
 import androidx.compose.ui.unit.dp
 import androidx.core.graphics.drawable.toBitmap
 import com.android.credentialmanager.R
-import com.android.credentialmanager.jetpack.CredentialEntryUi.Companion.TYPE_PASSWORD_CREDENTIAL
-import com.android.credentialmanager.jetpack.CredentialEntryUi.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
+import com.android.credentialmanager.jetpack.provider.CredentialEntryUi.Companion.TYPE_PASSWORD_CREDENTIAL
+import com.android.credentialmanager.jetpack.provider.CredentialEntryUi.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
 import com.android.credentialmanager.ui.theme.Grey100
 import com.android.credentialmanager.ui.theme.Shapes
 import com.android.credentialmanager.ui.theme.Typography
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt
new file mode 100644
index 0000000..7e7dbde
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.credentials.Credential
+import android.os.Bundle
+
+/**
+ * Base request class for registering a credential.
+ *
+ * @property type the credential type
+ * @property data the request data in the [Bundle] format
+ * @property requireSystemProvider true if must only be fulfilled by a system provider and false
+ *                              otherwise
+ */
+open class CreateCredentialRequest(
+        val type: String,
+        val data: Bundle,
+        val requireSystemProvider: Boolean,
+) {
+    companion object {
+        @JvmStatic
+        fun createFrom(from: android.credentials.CreateCredentialRequest): CreateCredentialRequest {
+            return try {
+                when (from.type) {
+                    Credential.TYPE_PASSWORD_CREDENTIAL ->
+                        CreatePasswordRequest.createFrom(from.data)
+                    PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
+                        CreatePublicKeyCredentialBaseRequest.createFrom(from.data)
+                    else ->
+                        CreateCredentialRequest(from.type, from.data, from.requireSystemProvider())
+                }
+            } catch (e: FrameworkClassParsingException) {
+                CreateCredentialRequest(from.type, from.data, from.requireSystemProvider())
+            }
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePasswordRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePasswordRequest.kt
new file mode 100644
index 0000000..f0da9f9
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePasswordRequest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.credentials.Credential
+import android.os.Bundle
+
+/**
+ * A request to save the user password credential with their password provider.
+ *
+ * @property id the user id associated with the password
+ * @property password the password
+ * @throws NullPointerException If [id] is null
+ * @throws NullPointerException If [password] is null
+ * @throws IllegalArgumentException If [password] is empty
+ */
+class CreatePasswordRequest constructor(
+        val id: String,
+        val password: String,
+) : CreateCredentialRequest(
+        Credential.TYPE_PASSWORD_CREDENTIAL,
+        toBundle(id, password),
+        false,
+) {
+
+    init {
+        require(password.isNotEmpty()) { "password should not be empty" }
+    }
+
+    companion object {
+        const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
+        const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"
+
+        @JvmStatic
+        internal fun toBundle(id: String, password: String): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_ID, id)
+            bundle.putString(BUNDLE_KEY_PASSWORD, password)
+            return bundle
+        }
+
+        @JvmStatic
+        fun createFrom(data: Bundle): CreatePasswordRequest {
+            try {
+                val id = data.getString(BUNDLE_KEY_ID)
+                val password = data.getString(BUNDLE_KEY_PASSWORD)
+                return CreatePasswordRequest(id!!, password!!)
+            } catch (e: Exception) {
+                throw FrameworkClassParsingException()
+            }
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialBaseRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialBaseRequest.kt
new file mode 100644
index 0000000..26d61f9
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialBaseRequest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.os.Bundle
+
+/**
+ * Base request class for registering a public key credential.
+ *
+ * @property requestJson The request in JSON format
+ * @throws NullPointerException If [requestJson] is null. This is handled by the Kotlin runtime
+ * @throws IllegalArgumentException If [requestJson] is empty
+ *
+ * @hide
+ */
+abstract class CreatePublicKeyCredentialBaseRequest constructor(
+        val requestJson: String,
+        type: String,
+        data: Bundle,
+        requireSystemProvider: Boolean,
+) : CreateCredentialRequest(type, data, requireSystemProvider) {
+
+    init {
+        require(requestJson.isNotEmpty()) { "request json must not be empty" }
+    }
+
+    companion object {
+        const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
+        const val BUNDLE_KEY_SUBTYPE = "androidx.credentials.BUNDLE_KEY_SUBTYPE"
+
+        @JvmStatic
+        fun createFrom(data: Bundle): CreatePublicKeyCredentialBaseRequest {
+            return when (data.getString(BUNDLE_KEY_SUBTYPE)) {
+                CreatePublicKeyCredentialRequest
+                        .BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST ->
+                    CreatePublicKeyCredentialRequestPrivileged.createFrom(data)
+                CreatePublicKeyCredentialRequestPrivileged
+                        .BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_PRIVILEGED ->
+                    CreatePublicKeyCredentialRequestPrivileged.createFrom(data)
+                else -> throw FrameworkClassParsingException()
+            }
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequest.kt
new file mode 100644
index 0000000..2eda90b
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequest.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.os.Bundle
+
+/**
+ * A request to register a passkey from the user's public key credential provider.
+ *
+ * @property requestJson the request in JSON format
+ * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request,
+ * true by default
+ * @throws NullPointerException If [requestJson] or [allowHybrid] is null. This is handled by the
+ * Kotlin runtime
+ * @throws IllegalArgumentException If [requestJson] is empty
+ *
+ * @hide
+ */
+class CreatePublicKeyCredentialRequest @JvmOverloads constructor(
+        requestJson: String,
+        @get:JvmName("allowHybrid")
+        val allowHybrid: Boolean = true
+) : CreatePublicKeyCredentialBaseRequest(
+        requestJson,
+        PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+        toBundle(requestJson, allowHybrid),
+        false,
+) {
+    companion object {
+        const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+        const val BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST =
+                "androidx.credentials.BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST"
+
+        @JvmStatic
+        internal fun toBundle(requestJson: String, allowHybrid: Boolean): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_SUBTYPE,
+                    BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST)
+            bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+            bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+            return bundle
+        }
+
+        @JvmStatic
+        fun createFrom(data: Bundle): CreatePublicKeyCredentialRequest {
+            try {
+                val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
+                val allowHybrid = data.get(BUNDLE_KEY_ALLOW_HYBRID)
+                return CreatePublicKeyCredentialRequest(requestJson!!, (allowHybrid!!) as Boolean)
+            } catch (e: Exception) {
+                throw FrameworkClassParsingException()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequestPrivileged.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequestPrivileged.kt
new file mode 100644
index 0000000..36324f8
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialRequestPrivileged.kt
@@ -0,0 +1,143 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.os.Bundle
+
+/**
+ * A privileged request to register a passkey from the user’s public key credential provider, where
+ * the caller can modify the rp. Only callers with privileged permission, e.g. user’s default
+ * brower, caBLE, can use this.
+ *
+ * @property requestJson the privileged request in JSON format
+ * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request,
+ * true by default
+ * @property rp the expected true RP ID which will override the one in the [requestJson]
+ * @property clientDataHash a hash that is used to verify the [rp] Identity
+ * @throws NullPointerException If any of [allowHybrid], [requestJson], [rp], or [clientDataHash] is
+ * null. This is handled by the Kotlin runtime
+ * @throws IllegalArgumentException If any of [requestJson], [rp], or [clientDataHash] is empty
+ *
+ * @hide
+ */
+class CreatePublicKeyCredentialRequestPrivileged @JvmOverloads constructor(
+        requestJson: String,
+        val rp: String,
+        val clientDataHash: String,
+        @get:JvmName("allowHybrid")
+        val allowHybrid: Boolean = true
+) : CreatePublicKeyCredentialBaseRequest(
+        requestJson,
+        PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+        toBundle(requestJson, rp, clientDataHash, allowHybrid),
+        false,
+) {
+
+    init {
+        require(rp.isNotEmpty()) { "rp must not be empty" }
+        require(clientDataHash.isNotEmpty()) { "clientDataHash must not be empty" }
+    }
+
+    /** A builder for [CreatePublicKeyCredentialRequestPrivileged]. */
+    class Builder(var requestJson: String, var rp: String, var clientDataHash: String) {
+
+        private var allowHybrid: Boolean = true
+
+        /**
+         * Sets the privileged request in JSON format.
+         */
+        fun setRequestJson(requestJson: String): Builder {
+            this.requestJson = requestJson
+            return this
+        }
+
+        /**
+         * Sets whether hybrid credentials are allowed to fulfill this request, true by default.
+         */
+        fun setAllowHybrid(allowHybrid: Boolean): Builder {
+            this.allowHybrid = allowHybrid
+            return this
+        }
+
+        /**
+         * Sets the expected true RP ID which will override the one in the [requestJson].
+         */
+        fun setRp(rp: String): Builder {
+            this.rp = rp
+            return this
+        }
+
+        /**
+         * Sets a hash that is used to verify the [rp] Identity.
+         */
+        fun setClientDataHash(clientDataHash: String): Builder {
+            this.clientDataHash = clientDataHash
+            return this
+        }
+
+        /** Builds a [CreatePublicKeyCredentialRequestPrivileged]. */
+        fun build(): CreatePublicKeyCredentialRequestPrivileged {
+            return CreatePublicKeyCredentialRequestPrivileged(this.requestJson,
+                    this.rp, this.clientDataHash, this.allowHybrid)
+        }
+    }
+
+    companion object {
+        const val BUNDLE_KEY_RP = "androidx.credentials.BUNDLE_KEY_RP"
+        const val BUNDLE_KEY_CLIENT_DATA_HASH =
+                "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH"
+        const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+        const val BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_PRIVILEGED =
+                "androidx.credentials.BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_" +
+                        "PRIVILEGED"
+
+        @JvmStatic
+        internal fun toBundle(
+                requestJson: String,
+                rp: String,
+                clientDataHash: String,
+                allowHybrid: Boolean
+        ): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_SUBTYPE,
+                    BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_PRIVILEGED)
+            bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+            bundle.putString(BUNDLE_KEY_RP, rp)
+            bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
+            bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+            return bundle
+        }
+
+        @JvmStatic
+        fun createFrom(data: Bundle): CreatePublicKeyCredentialRequestPrivileged {
+            try {
+                val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
+                val rp = data.getString(BUNDLE_KEY_RP)
+                val clientDataHash = data.getString(BUNDLE_KEY_CLIENT_DATA_HASH)
+                val allowHybrid = data.get(BUNDLE_KEY_ALLOW_HYBRID)
+                return CreatePublicKeyCredentialRequestPrivileged(
+                        requestJson!!,
+                        rp!!,
+                        clientDataHash!!,
+                        (allowHybrid!!) as Boolean,
+                )
+            } catch (e: Exception) {
+                throw FrameworkClassParsingException()
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/Credential.kt
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
copy to packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/Credential.kt
index 581dafa3..ee08e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/Credential.kt
@@ -14,10 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.model
+package com.android.credentialmanager.jetpack.developer
 
-/** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
-enum class KeyguardQuickAffordancePosition {
-    BOTTOM_START,
-    BOTTOM_END,
-}
+import android.os.Bundle
+
+/**
+ * Base class for a credential with which the user consented to authenticate to the app.
+ *
+ * @property type the credential type
+ * @property data the credential data in the [Bundle] format.
+ */
+open class Credential(val type: String, val data: Bundle)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/FrameworkClassParsingException.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
copy to packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/FrameworkClassParsingException.kt
index 581dafa3..497c272 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/FrameworkClassParsingException.kt
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.model
+package com.android.credentialmanager.jetpack.developer
 
-/** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
-enum class KeyguardQuickAffordancePosition {
-    BOTTOM_START,
-    BOTTOM_END,
-}
+/**
+ * Internal exception used to indicate a parsing error while converting from a framework type to
+ * a jetpack type.
+ *
+ * @hide
+ */
+internal class FrameworkClassParsingException : Exception()
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt
new file mode 100644
index 0000000..eb65241
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.credentials.Credential
+import android.os.Bundle
+
+/**
+ * Base request class for getting a registered credential.
+ *
+ * @property type the credential type
+ * @property data the request data in the [Bundle] format
+ * @property requireSystemProvider true if must only be fulfilled by a system provider and false
+ *                              otherwise
+ */
+open class GetCredentialOption(
+        val type: String,
+        val data: Bundle,
+        val requireSystemProvider: Boolean,
+) {
+    companion object {
+        @JvmStatic
+        fun createFrom(from: android.credentials.GetCredentialOption): GetCredentialOption {
+            return try {
+                when (from.type) {
+                    Credential.TYPE_PASSWORD_CREDENTIAL ->
+                        GetPasswordOption.createFrom(from.data)
+                    PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
+                        GetPublicKeyCredentialBaseOption.createFrom(from.data)
+                    else ->
+                        GetCredentialOption(from.type, from.data, from.requireSystemProvider())
+                }
+            } catch (e: FrameworkClassParsingException) {
+                GetCredentialOption(from.type, from.data, from.requireSystemProvider())
+            }
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialRequest.kt
new file mode 100644
index 0000000..7f9256e
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialRequest.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+/**
+ * Encapsulates a request to get a user credential.
+ *
+ * @property getCredentialOptions the list of [GetCredentialOption] from which the user can choose
+ * one to authenticate to the app
+ * @throws IllegalArgumentException If [getCredentialOptions] is empty
+ */
+class GetCredentialRequest constructor(
+        val getCredentialOptions: List<GetCredentialOption>,
+) {
+
+    init {
+        require(getCredentialOptions.isNotEmpty()) { "credentialRequests should not be empty" }
+    }
+
+    /** A builder for [GetCredentialRequest]. */
+    class Builder {
+        private var getCredentialOptions: MutableList<GetCredentialOption> = mutableListOf()
+
+        /** Adds a specific type of [GetCredentialOption]. */
+        fun addGetCredentialOption(getCredentialOption: GetCredentialOption): Builder {
+            getCredentialOptions.add(getCredentialOption)
+            return this
+        }
+
+        /** Sets the list of [GetCredentialOption]. */
+        fun setGetCredentialOptions(getCredentialOptions: List<GetCredentialOption>): Builder {
+            this.getCredentialOptions = getCredentialOptions.toMutableList()
+            return this
+        }
+
+        /**
+         * Builds a [GetCredentialRequest].
+         *
+         * @throws IllegalArgumentException If [getCredentialOptions] is empty
+         */
+        fun build(): GetCredentialRequest {
+            return GetCredentialRequest(getCredentialOptions.toList())
+        }
+    }
+
+    companion object {
+        @JvmStatic
+        fun createFrom(from: android.credentials.GetCredentialRequest): GetCredentialRequest {
+            return GetCredentialRequest(
+                    from.getCredentialOptions.map {GetCredentialOption.createFrom(it)}
+            )
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPasswordOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPasswordOption.kt
new file mode 100644
index 0000000..2facad1
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPasswordOption.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.credentials.Credential
+import android.os.Bundle
+
+/** A request to retrieve the user's saved application password from their password provider. */
+class GetPasswordOption : GetCredentialOption(
+        Credential.TYPE_PASSWORD_CREDENTIAL,
+        Bundle(),
+        false,
+) {
+    companion object {
+        @JvmStatic
+        fun createFrom(data: Bundle): GetPasswordOption {
+            return GetPasswordOption()
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialBaseOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialBaseOption.kt
new file mode 100644
index 0000000..9b51b30
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialBaseOption.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.os.Bundle
+
+/**
+ * Base request class for getting a registered public key credential.
+ *
+ * @property requestJson the request in JSON format
+ * @throws NullPointerException If [requestJson] is null - auto handled by the
+ * Kotlin runtime
+ * @throws IllegalArgumentException If [requestJson] is empty
+ *
+ * @hide
+ */
+abstract class GetPublicKeyCredentialBaseOption constructor(
+        val requestJson: String,
+        type: String,
+        data: Bundle,
+        requireSystemProvider: Boolean,
+) : GetCredentialOption(type, data, requireSystemProvider) {
+
+    init {
+        require(requestJson.isNotEmpty()) { "request json must not be empty" }
+    }
+
+    companion object {
+        const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
+        const val BUNDLE_KEY_SUBTYPE = "androidx.credentials.BUNDLE_KEY_SUBTYPE"
+
+        @JvmStatic
+        fun createFrom(data: Bundle): GetPublicKeyCredentialBaseOption {
+            return when (data.getString(BUNDLE_KEY_SUBTYPE)) {
+                GetPublicKeyCredentialOption
+                        .BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION ->
+                    GetPublicKeyCredentialOption.createFrom(data)
+                GetPublicKeyCredentialOptionPrivileged
+                        .BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION_PRIVILEGED ->
+                    GetPublicKeyCredentialOptionPrivileged.createFrom(data)
+                else -> throw FrameworkClassParsingException()
+            }
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOption.kt
new file mode 100644
index 0000000..6f13c17
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOption.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.os.Bundle
+
+/**
+ * A request to get passkeys from the user's public key credential provider.
+ *
+ * @property requestJson the request in JSON format
+ * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request,
+ * true by default
+ * @throws NullPointerException If [requestJson] or [allowHybrid] is null. It is handled by the
+ * Kotlin runtime
+ * @throws IllegalArgumentException If [requestJson] is empty
+ *
+ * @hide
+ */
+class GetPublicKeyCredentialOption @JvmOverloads constructor(
+        requestJson: String,
+        @get:JvmName("allowHybrid")
+        val allowHybrid: Boolean = true,
+) : GetPublicKeyCredentialBaseOption(
+        requestJson,
+        PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+        toBundle(requestJson, allowHybrid),
+        false
+) {
+    companion object {
+        const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+        const val BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION =
+                "androidx.credentials.BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION"
+
+        @JvmStatic
+        internal fun toBundle(requestJson: String, allowHybrid: Boolean): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+            bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+            return bundle
+        }
+
+        @JvmStatic
+        fun createFrom(data: Bundle): GetPublicKeyCredentialOption {
+            try {
+                val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
+                val allowHybrid = data.get(BUNDLE_KEY_ALLOW_HYBRID)
+                return GetPublicKeyCredentialOption(requestJson!!, (allowHybrid!!) as Boolean)
+            } catch (e: Exception) {
+                throw FrameworkClassParsingException()
+            }
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOptionPrivileged.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOptionPrivileged.kt
new file mode 100644
index 0000000..79c62a1
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetPublicKeyCredentialOptionPrivileged.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.os.Bundle
+
+/**
+ * A privileged request to get passkeys from the user's public key credential provider. The caller
+ * can modify the RP. Only callers with privileged permission (e.g. user's public browser or caBLE)
+ * can use this.
+ *
+ * @property requestJson the privileged request in JSON format
+ * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request,
+ * true by default
+ * @property rp the expected true RP ID which will override the one in the [requestJson]
+ * @property clientDataHash a hash that is used to verify the [rp] Identity
+ * @throws NullPointerException If any of [allowHybrid], [requestJson], [rp], or [clientDataHash]
+ * is null. This is handled by the Kotlin runtime
+ * @throws IllegalArgumentException If any of [requestJson], [rp], or [clientDataHash] is empty
+ *
+ * @hide
+ */
+class GetPublicKeyCredentialOptionPrivileged @JvmOverloads constructor(
+        requestJson: String,
+        val rp: String,
+        val clientDataHash: String,
+        @get:JvmName("allowHybrid")
+        val allowHybrid: Boolean = true
+) : GetPublicKeyCredentialBaseOption(
+        requestJson,
+        PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+        toBundle(requestJson, rp, clientDataHash, allowHybrid),
+        false,
+) {
+
+    init {
+        require(rp.isNotEmpty()) { "rp must not be empty" }
+        require(clientDataHash.isNotEmpty()) { "clientDataHash must not be empty" }
+    }
+
+    /** A builder for [GetPublicKeyCredentialOptionPrivileged]. */
+    class Builder(var requestJson: String, var rp: String, var clientDataHash: String) {
+
+        private var allowHybrid: Boolean = true
+
+        /**
+         * Sets the privileged request in JSON format.
+         */
+        fun setRequestJson(requestJson: String): Builder {
+            this.requestJson = requestJson
+            return this
+        }
+
+        /**
+         * Sets whether hybrid credentials are allowed to fulfill this request, true by default.
+         */
+        fun setAllowHybrid(allowHybrid: Boolean): Builder {
+            this.allowHybrid = allowHybrid
+            return this
+        }
+
+        /**
+         * Sets the expected true RP ID which will override the one in the [requestJson].
+         */
+        fun setRp(rp: String): Builder {
+            this.rp = rp
+            return this
+        }
+
+        /**
+         * Sets a hash that is used to verify the [rp] Identity.
+         */
+        fun setClientDataHash(clientDataHash: String): Builder {
+            this.clientDataHash = clientDataHash
+            return this
+        }
+
+        /** Builds a [GetPublicKeyCredentialOptionPrivileged]. */
+        fun build(): GetPublicKeyCredentialOptionPrivileged {
+            return GetPublicKeyCredentialOptionPrivileged(this.requestJson,
+                    this.rp, this.clientDataHash, this.allowHybrid)
+        }
+    }
+
+    companion object {
+        const val BUNDLE_KEY_RP = "androidx.credentials.BUNDLE_KEY_RP"
+        const val BUNDLE_KEY_CLIENT_DATA_HASH =
+                "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH"
+        const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+        const val BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION_PRIVILEGED =
+                "androidx.credentials.BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION" +
+                        "_PRIVILEGED"
+
+        @JvmStatic
+        internal fun toBundle(
+                requestJson: String,
+                rp: String,
+                clientDataHash: String,
+                allowHybrid: Boolean
+        ): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+            bundle.putString(BUNDLE_KEY_RP, rp)
+            bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
+            bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+            return bundle
+        }
+
+        @JvmStatic
+        fun createFrom(data: Bundle): GetPublicKeyCredentialOptionPrivileged {
+            try {
+                val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
+                val rp = data.getString(BUNDLE_KEY_RP)
+                val clientDataHash = data.getString(BUNDLE_KEY_CLIENT_DATA_HASH)
+                val allowHybrid = data.get(BUNDLE_KEY_ALLOW_HYBRID)
+                return GetPublicKeyCredentialOptionPrivileged(
+                        requestJson!!,
+                        rp!!,
+                        clientDataHash!!,
+                        (allowHybrid!!) as Boolean,
+                )
+            } catch (e: Exception) {
+                throw FrameworkClassParsingException()
+            }
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PublicKeyCredential.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PublicKeyCredential.kt
new file mode 100644
index 0000000..b45a63b
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PublicKeyCredential.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.credentialmanager.jetpack.developer
+
+import android.os.Bundle
+
+/**
+ * Represents the user's passkey credential granted by the user for app sign-in.
+ *
+ * @property authenticationResponseJson the public key credential authentication response in
+ * JSON format that follows the standard webauthn json format shown at
+ * [this w3c link](https://w3c.github.io/webauthn/#dictdef-authenticationresponsejson)
+ * @throws NullPointerException If [authenticationResponseJson] is null. This is handled by the
+ * kotlin runtime
+ * @throws IllegalArgumentException If [authenticationResponseJson] is empty
+ *
+ * @hide
+ */
+class PublicKeyCredential constructor(
+        val authenticationResponseJson: String
+) : Credential(
+        TYPE_PUBLIC_KEY_CREDENTIAL,
+        toBundle(authenticationResponseJson)
+) {
+
+    init {
+        require(authenticationResponseJson.isNotEmpty()) {
+            "authentication response JSON must not be empty" }
+    }
+    companion object {
+        /** The type value for public key credential related operations. */
+        const val TYPE_PUBLIC_KEY_CREDENTIAL: String =
+                "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"
+        const val BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON =
+                "androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON"
+
+        @JvmStatic
+        internal fun toBundle(authenticationResponseJson: String): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON, authenticationResponseJson)
+            return bundle
+        }
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/ActionUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt
similarity index 96%
rename from packages/CredentialManager/src/com/android/credentialmanager/jetpack/ActionUi.kt
rename to packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt
index d4341b4..1e639fe 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/ActionUi.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.credentialmanager.jetpack
+package com.android.credentialmanager.jetpack.provider
 
 import android.app.slice.Slice
 import android.credentials.ui.Entry
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/CredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt
similarity index 96%
rename from packages/CredentialManager/src/com/android/credentialmanager/jetpack/CredentialEntryUi.kt
rename to packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt
index d6f1b5f..12ab436 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/CredentialEntryUi.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.credentialmanager.jetpack
+package com.android.credentialmanager.jetpack.provider
 
 import android.app.slice.Slice
 import android.graphics.drawable.Icon
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasskeyCredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasskeyCredentialEntryUi.kt
similarity index 97%
rename from packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasskeyCredentialEntryUi.kt
rename to packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasskeyCredentialEntryUi.kt
index bb3b206..c5dbe66 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasskeyCredentialEntryUi.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasskeyCredentialEntryUi.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.credentialmanager.jetpack
+package com.android.credentialmanager.jetpack.provider
 
 import android.app.slice.Slice
 import android.credentials.ui.Entry
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasswordCredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasswordCredentialEntryUi.kt
similarity index 97%
rename from packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasswordCredentialEntryUi.kt
rename to packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasswordCredentialEntryUi.kt
index 7311b70..5049503 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/PasswordCredentialEntryUi.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/PasswordCredentialEntryUi.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.credentialmanager.jetpack
+package com.android.credentialmanager.jetpack.provider
 
 import android.app.slice.Slice
 import android.credentials.ui.Entry
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/SaveEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt
similarity index 97%
rename from packages/CredentialManager/src/com/android/credentialmanager/jetpack/SaveEntryUi.kt
rename to packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt
index fad3309..b260cf6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/SaveEntryUi.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.credentialmanager.jetpack
+package com.android.credentialmanager.jetpack.provider
 
 import android.app.slice.Slice
 import android.credentials.ui.Entry
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index c659525..f170ead 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -54,6 +54,7 @@
         "SettingsLibSettingsTransition",
         "SettingsLibButtonPreference",
         "SettingsLibDeviceStateRotationLock",
+        "SettingsLibProfileSelector",
         "setupdesign",
         "zxing-core-1.7",
         "androidx.room_room-runtime",
diff --git a/packages/SettingsLib/ProfileSelector/Android.bp b/packages/SettingsLib/ProfileSelector/Android.bp
new file mode 100644
index 0000000..250cd75
--- /dev/null
+++ b/packages/SettingsLib/ProfileSelector/Android.bp
@@ -0,0 +1,27 @@
+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_library {
+    name: "SettingsLibProfileSelector",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+        "com.google.android.material_material",
+        "SettingsLibSettingsTheme",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "23",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.mediaprovider",
+    ],
+}
diff --git a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml b/packages/SettingsLib/ProfileSelector/AndroidManifest.xml
similarity index 70%
rename from packages/SettingsLib/Spa/spa/res/values-night/themes.xml
rename to packages/SettingsLib/ProfileSelector/AndroidManifest.xml
index 67dd2b0..a57469e 100644
--- a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
+++ b/packages/SettingsLib/ProfileSelector/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2022 The Android Open Source Project
+  Copyright (C) 2020 The Android Open Source Project
 
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -13,8 +13,10 @@
   WITHOUT 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>
+  -->
 
-    <style name="Theme.SpaLib.DayNight" />
-</resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="23" />
+</manifest>
diff --git a/packages/SettingsLib/res/color-night-v31/settingslib_tabs_indicator_color.xml b/packages/SettingsLib/ProfileSelector/res/color-night/settingslib_tabs_indicator_color.xml
similarity index 100%
rename from packages/SettingsLib/res/color-night-v31/settingslib_tabs_indicator_color.xml
rename to packages/SettingsLib/ProfileSelector/res/color-night/settingslib_tabs_indicator_color.xml
diff --git a/packages/SettingsLib/res/color-night-v31/settingslib_tabs_text_color.xml b/packages/SettingsLib/ProfileSelector/res/color-night/settingslib_tabs_text_color.xml
similarity index 100%
rename from packages/SettingsLib/res/color-night-v31/settingslib_tabs_text_color.xml
rename to packages/SettingsLib/ProfileSelector/res/color-night/settingslib_tabs_text_color.xml
diff --git a/packages/SettingsLib/res/color-v31/settingslib_tabs_indicator_color.xml b/packages/SettingsLib/ProfileSelector/res/color/settingslib_tabs_indicator_color.xml
similarity index 100%
rename from packages/SettingsLib/res/color-v31/settingslib_tabs_indicator_color.xml
rename to packages/SettingsLib/ProfileSelector/res/color/settingslib_tabs_indicator_color.xml
diff --git a/packages/SettingsLib/res/color-v31/settingslib_tabs_text_color.xml b/packages/SettingsLib/ProfileSelector/res/color/settingslib_tabs_text_color.xml
similarity index 100%
rename from packages/SettingsLib/res/color-v31/settingslib_tabs_text_color.xml
rename to packages/SettingsLib/ProfileSelector/res/color/settingslib_tabs_text_color.xml
diff --git a/packages/SettingsLib/res/drawable-v31/settingslib_tabs_background.xml b/packages/SettingsLib/ProfileSelector/res/drawable/settingslib_tabs_background.xml
similarity index 100%
rename from packages/SettingsLib/res/drawable-v31/settingslib_tabs_background.xml
rename to packages/SettingsLib/ProfileSelector/res/drawable/settingslib_tabs_background.xml
diff --git a/packages/SettingsLib/res/drawable-v31/settingslib_tabs_indicator_background.xml b/packages/SettingsLib/ProfileSelector/res/drawable/settingslib_tabs_indicator_background.xml
similarity index 100%
rename from packages/SettingsLib/res/drawable-v31/settingslib_tabs_indicator_background.xml
rename to packages/SettingsLib/ProfileSelector/res/drawable/settingslib_tabs_indicator_background.xml
diff --git a/packages/SettingsLib/ProfileSelector/res/layout/tab_fragment.xml b/packages/SettingsLib/ProfileSelector/res/layout/tab_fragment.xml
new file mode 100644
index 0000000..0448c6c
--- /dev/null
+++ b/packages/SettingsLib/ProfileSelector/res/layout/tab_fragment.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:theme="@style/Theme.MaterialComponents.DayNight"
+    android:id="@+id/tab_container"
+    android:clipToPadding="true"
+    android:clipChildren="true"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <com.google.android.material.tabs.TabLayout
+        android:id="@+id/tabs"
+        style="@style/SettingsLibTabsStyle"/>
+
+    <androidx.viewpager2.widget.ViewPager2
+        android:id="@+id/view_pager"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    </androidx.viewpager2.widget.ViewPager2>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml b/packages/SettingsLib/ProfileSelector/res/values/strings.xml
similarity index 65%
copy from packages/SettingsLib/Spa/spa/res/values-night/themes.xml
copy to packages/SettingsLib/ProfileSelector/res/values/strings.xml
index 67dd2b0..68d4047 100644
--- a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values/strings.xml
@@ -13,8 +13,12 @@
   WITHOUT 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>
+  -->
 
-    <style name="Theme.SpaLib.DayNight" />
-</resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Header for items under the personal user [CHAR LIMIT=30] -->
+    <string name="settingslib_category_personal">Personal</string>
+    <!-- Header for items under the work user [CHAR LIMIT=30] -->
+    <string name="settingslib_category_work">Work</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-v31/styles.xml b/packages/SettingsLib/ProfileSelector/res/values/styles.xml
similarity index 100%
rename from packages/SettingsLib/res/values-v31/styles.xml
rename to packages/SettingsLib/ProfileSelector/res/values/styles.xml
diff --git a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java
new file mode 100644
index 0000000..ac426ed
--- /dev/null
+++ b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java
@@ -0,0 +1,118 @@
+/*
+ * 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.settingslib.widget;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.fragment.app.Fragment;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.google.android.material.tabs.TabLayout;
+import com.google.android.material.tabs.TabLayoutMediator;
+
+/**
+ * Base fragment class for profile settings.
+ */
+public abstract class ProfileSelectFragment extends Fragment {
+
+    /**
+     * Personal or Work profile tab of {@link ProfileSelectFragment}
+     * <p>0: Personal tab.
+     * <p>1: Work profile tab.
+     */
+    public static final String EXTRA_SHOW_FRAGMENT_TAB =
+            ":settings:show_fragment_tab";
+
+    /**
+     * Used in fragment argument with Extra key EXTRA_SHOW_FRAGMENT_TAB
+     */
+    public static final int PERSONAL_TAB = 0;
+
+    /**
+     * Used in fragment argument with Extra key EXTRA_SHOW_FRAGMENT_TAB
+     */
+    public static final int WORK_TAB = 1;
+
+    private ViewGroup mContentView;
+
+    private ViewPager2 mViewPager;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        // Defines the xml file for the fragment
+        mContentView = (ViewGroup) inflater.inflate(R.layout.tab_fragment, container, false);
+
+        final Activity activity = getActivity();
+        final int titleResId = getTitleResId();
+        if (titleResId > 0) {
+            activity.setTitle(titleResId);
+        }
+        final int selectedTab = getTabId(activity, getArguments());
+
+        final View tabContainer = mContentView.findViewById(R.id.tab_container);
+        mViewPager = tabContainer.findViewById(R.id.view_pager);
+        mViewPager.setAdapter(new ProfileViewPagerAdapter(this));
+        final TabLayout tabs = tabContainer.findViewById(R.id.tabs);
+        new TabLayoutMediator(tabs, mViewPager,
+                (tab, position) -> tab.setText(getPageTitle(position))
+        ).attach();
+
+        tabContainer.setVisibility(View.VISIBLE);
+        final TabLayout.Tab tab = tabs.getTabAt(selectedTab);
+        tab.select();
+
+        return mContentView;
+    }
+
+    /**
+     * create Personal or Work profile fragment
+     * <p>0: Personal profile.
+     * <p>1: Work profile.
+     */
+    public abstract Fragment createFragment(int position);
+
+    /**
+     * Returns a resource ID of the title
+     * Override this if the title needs to be updated dynamically.
+     */
+    public int getTitleResId() {
+        return 0;
+    }
+
+    int getTabId(Activity activity, Bundle bundle) {
+        if (bundle != null) {
+            final int extraTab = bundle.getInt(EXTRA_SHOW_FRAGMENT_TAB, -1);
+            if (extraTab != -1) {
+                return extraTab;
+            }
+        }
+        return PERSONAL_TAB;
+    }
+
+    private CharSequence getPageTitle(int position) {
+        if (position == WORK_TAB) {
+            return getContext().getString(R.string.settingslib_category_work);
+        }
+
+        return getString(R.string.settingslib_category_personal);
+    }
+}
diff --git a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java
new file mode 100644
index 0000000..daf2564
--- /dev/null
+++ b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.settingslib.widget;
+
+import androidx.fragment.app.Fragment;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+
+/**
+ * ViewPager Adapter to handle between TabLayout and ViewPager2
+ */
+public class ProfileViewPagerAdapter extends FragmentStateAdapter {
+
+    private final ProfileSelectFragment mParentFragments;
+
+    ProfileViewPagerAdapter(ProfileSelectFragment fragment) {
+        super(fragment);
+        mParentFragments = fragment;
+    }
+
+    @Override
+    public Fragment createFragment(int position) {
+        return mParentFragments.createFragment(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return 2;
+    }
+}
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
index bcc64d3..b5a21bd 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
@@ -23,5 +23,6 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.permission",
+        "com.android.mediaprovider",
     ],
 }
diff --git a/packages/SettingsLib/SettingsTheme/Android.bp b/packages/SettingsLib/SettingsTheme/Android.bp
index 82e0220..939977f 100644
--- a/packages/SettingsLib/SettingsTheme/Android.bp
+++ b/packages/SettingsLib/SettingsTheme/Android.bp
@@ -24,5 +24,6 @@
         "com.android.permission",
         "com.android.adservices",
         "com.android.healthconnect",
+        "com.android.mediaprovider",
     ],
 }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryApplication.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryApplication.kt
index 36b58ad..dfbf244 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryApplication.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryApplication.kt
@@ -22,6 +22,6 @@
 class GalleryApplication : Application() {
     override fun onCreate() {
         super.onCreate()
-        SpaEnvironmentFactory.reset(GallerySpaEnvironment)
+        SpaEnvironmentFactory.reset(GallerySpaEnvironment(this))
     }
 }
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 4af2589..92f4fe4 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.spa.gallery
 
+import android.content.Context
 import com.android.settingslib.spa.framework.common.LocalLogger
 import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
 import com.android.settingslib.spa.framework.common.SpaEnvironment
@@ -49,7 +50,7 @@
     // Add your SPPs
 }
 
-object GallerySpaEnvironment : SpaEnvironment() {
+class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
     override val pageProviderRepository = lazy {
         SettingsPageProviderRepository(
             allPageProviders = listOf(
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
index 7fd49db..04d0fe0 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
@@ -18,10 +18,10 @@
 
 import android.os.Bundle
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.tooling.preview.Preview
 import com.android.settingslib.spa.framework.common.SettingsEntry
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.gallery.R
@@ -59,9 +59,13 @@
         )
     }
 
+    override fun getTitle(arguments: Bundle?): String {
+        return SpaEnvironmentFactory.instance.appContext.getString(R.string.app_name)
+    }
+
     @Composable
     override fun Page(arguments: Bundle?) {
-        HomeScaffold(title = stringResource(R.string.app_name)) {
+        HomeScaffold(title = getTitle(arguments)) {
             for (entry in buildEntry(arguments)) {
                 if (entry.owner.isCreateBy(SettingsPageProviderEnum.ARGUMENT.name)) {
                     entry.UiLayout(ArgumentPageModel.buildArgument(intParam = 0))
@@ -76,6 +80,7 @@
 @Preview(showBackground = true)
 @Composable
 private fun HomeScreenPreview() {
+    SpaEnvironmentFactory.resetForPreview()
     SettingsTheme {
         HomePageProvider.Page(null)
     }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
index 8207310..7958d11 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
@@ -23,6 +23,7 @@
 import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
 import com.android.settingslib.spa.framework.common.SettingsPage
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
@@ -98,9 +99,13 @@
             }
     }
 
+    override fun getTitle(arguments: Bundle?): String {
+        return ArgumentPageModel.genPageTitle()
+    }
+
     @Composable
     override fun Page(arguments: Bundle?) {
-        RegularScaffold(title = ArgumentPageModel.create(arguments).genPageTitle()) {
+        RegularScaffold(title = getTitle(arguments)) {
             for (entry in buildEntry(arguments)) {
                 if (entry.toPage != null) {
                     entry.UiLayout(ArgumentPageModel.buildNextArgument(arguments))
@@ -115,6 +120,7 @@
 @Preview(showBackground = true)
 @Composable
 private fun ArgumentPagePreview() {
+    SpaEnvironmentFactory.resetForPreview()
     SettingsTheme {
         ArgumentPageProvider.Page(
             ArgumentPageModel.buildArgument(stringParam = "foo", intParam = 0)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
index e5e3c67..5f15865 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
@@ -81,6 +81,10 @@
             return EntrySearchData(title = PAGE_TITLE, keyword = ARGUMENT_PAGE_KEYWORDS)
         }
 
+        fun genPageTitle(): String {
+            return PAGE_TITLE
+        }
+
         @Composable
         fun create(arguments: Bundle?): ArgumentPageModel {
             val pageModel: ArgumentPageModel = viewModel(key = arguments.toString())
@@ -89,7 +93,6 @@
         }
     }
 
-    private val title = PAGE_TITLE
     private var arguments: Bundle? = null
     private var stringParam: String? = null
     private var intParam: Int? = null
@@ -104,11 +107,6 @@
     }
 
     @Composable
-    fun genPageTitle(): String {
-        return title
-    }
-
-    @Composable
     fun genStringParamPreferenceModel(): PreferenceModel {
         return object : PreferenceModel {
             override val title = STRING_PARAM_TITLE
@@ -131,7 +129,7 @@
             "$INT_PARAM_NAME=" + intParam!!
         )
         return object : PreferenceModel {
-            override val title = genPageTitle()
+            override val title = PAGE_TITLE
             override val summary = stateOf(summaryArray.joinToString(", "))
             override val onClick = navigator(
                 SettingsPageProviderEnum.ARGUMENT.displayName + parameter.navLink(arguments)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt
index 0fc2a5f..c903cfd 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt
@@ -67,9 +67,13 @@
             }
     }
 
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+
     @Composable
     override fun Page(arguments: Bundle?) {
-        RegularScaffold(title = TITLE) {
+        RegularScaffold(title = getTitle(arguments)) {
             for (entry in buildEntry(arguments)) {
                 entry.UiLayout()
             }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt
index a64d4a5..e10cf3a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt
@@ -31,7 +31,6 @@
 import com.android.settingslib.spa.widget.ResourceType
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
-import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 
 private const val TITLE = "Sample Illustration"
 
@@ -82,13 +81,8 @@
             }
     }
 
-    @Composable
-    override fun Page(arguments: Bundle?) {
-        RegularScaffold(title = TITLE) {
-            for (entry in buildEntry(arguments)) {
-                entry.UiLayout()
-            }
-        }
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
     }
 }
 
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ProgressBarPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ProgressBarPage.kt
index dc45df4..9136b04 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ProgressBarPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ProgressBarPage.kt
@@ -60,6 +60,10 @@
             }
     }
 
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+
     @Composable
     override fun Page(arguments: Bundle?) {
         // Mocks a loading time of 2 seconds.
@@ -69,7 +73,7 @@
             loading = false
         }
 
-        RegularScaffold(title = TITLE) {
+        RegularScaffold(title = getTitle(arguments)) {
             // Auto update the progress and finally jump tp 0.4f.
             var progress by remember { mutableStateOf(0f) }
             LaunchedEffect(Unit) {
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
index b38178b..cb58a95 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
@@ -49,9 +49,13 @@
             }
     }
 
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+
     @Composable
     override fun Page(arguments: Bundle?) {
-        SettingsScaffold(title = TITLE) { paddingValues ->
+        SettingsScaffold(title = getTitle(arguments)) { paddingValues ->
             Box(Modifier.padding(paddingValues)) {
                 SettingsPager(listOf("Personal", "Work")) {
                     PlaceholderTitle("Page $it")
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
index 7567c6d..73b34a5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
@@ -37,7 +37,6 @@
 import com.android.settingslib.spa.widget.preference.SliderPreferenceModel
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
-import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 
 private const val TITLE = "Sample Slider"
 
@@ -119,13 +118,8 @@
             }
     }
 
-    @Composable
-    override fun Page(arguments: Bundle?) {
-        RegularScaffold(title = TITLE) {
-            for (entry in buildEntry(arguments)) {
-                entry.UiLayout()
-            }
-        }
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
     }
 }
 
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt
index a8e4938..f38a8d4 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt
@@ -33,7 +33,6 @@
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
-import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 
 private const val TITLE = "Sample MainSwitchPreference"
 
@@ -72,13 +71,8 @@
             }
     }
 
-    @Composable
-    override fun Page(arguments: Bundle?) {
-        RegularScaffold(title = TITLE) {
-            for (entry in buildEntry(arguments)) {
-                entry.UiLayout()
-            }
-        }
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
     }
 }
 
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
index 165eaa0..61925a7 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
@@ -17,7 +17,6 @@
 package com.android.settingslib.spa.gallery.preference
 
 import android.os.Bundle
-import androidx.compose.runtime.Composable
 import com.android.settingslib.spa.framework.common.SettingsEntry
 import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
@@ -25,7 +24,6 @@
 import com.android.settingslib.spa.framework.compose.navigator
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
-import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 
 private const val TITLE = "Category: Preference"
 
@@ -54,12 +52,7 @@
             }
     }
 
-    @Composable
-    override fun Page(arguments: Bundle?) {
-        RegularScaffold(title = TITLE) {
-            for (entry in buildEntry(arguments)) {
-                entry.UiLayout()
-            }
-        }
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
     }
 }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
index fa8d51c..26e59ff 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
@@ -49,7 +49,6 @@
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.preference.SimplePreferenceMacro
-import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 import com.android.settingslib.spa.widget.ui.SettingsIcon
 
 private const val TAG = "PreferencePage"
@@ -128,11 +127,11 @@
                 .setStatusDataFn { EntryStatusData(isDisabled = false) }
                 .setUiLayoutFn {
                     val model = PreferencePageModel.create()
-                    val asyncSummary = remember { model.getAsyncSummary() }
                     Preference(
                         object : PreferenceModel {
                             override val title = ASYNC_PREFERENCE_TITLE
-                            override val summary = asyncSummary
+                            override val summary = model.asyncSummary
+                            override val enabled = model.asyncEnable
                         }
                     )
                 }.build()
@@ -204,19 +203,15 @@
             }
     }
 
-    @Composable
-    override fun Page(arguments: Bundle?) {
-        RegularScaffold(title = PAGE_TITLE) {
-            for (entry in buildEntry(arguments)) {
-                entry.UiLayout()
-            }
-        }
+    override fun getTitle(arguments: Bundle?): String {
+        return PAGE_TITLE
     }
 }
 
 @Preview(showBackground = true)
 @Composable
 private fun PreferencePagePreview() {
+    SpaEnvironmentFactory.resetForPreview()
     SettingsTheme {
         PreferencePageProvider.Page(null)
     }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt
index 1e64b2e..d874417 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt
@@ -59,7 +59,8 @@
 
     private val spaLogger = SpaEnvironmentFactory.instance.logger
 
-    private val asyncSummary = mutableStateOf(" ")
+    val asyncSummary = mutableStateOf("(loading)")
+    val asyncEnable = mutableStateOf(false)
 
     private val manualUpdater = mutableStateOf(0)
 
@@ -87,16 +88,13 @@
     override fun initialize(arguments: Bundle?) {
         spaLogger.message(TAG, "initialize with args " + arguments.toString())
         viewModelScope.launch(Dispatchers.IO) {
+            // Loading your data here.
             delay(2000L)
             asyncSummary.value = ASYNC_PREFERENCE_SUMMARY
+            asyncEnable.value = true
         }
     }
 
-    fun getAsyncSummary(): State<String> {
-        spaLogger.message(TAG, "getAsyncSummary")
-        return asyncSummary
-    }
-
     fun getManualUpdaterSummary(): State<String> {
         spaLogger.message(TAG, "getManualUpdaterSummary")
         return derivedStateOf { manualUpdater.value.toString() }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt
index 46b44ca..367766a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt
@@ -34,7 +34,6 @@
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.preference.SwitchPreference
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
-import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 import kotlinx.coroutines.delay
 
 private const val TITLE = "Sample SwitchPreference"
@@ -88,13 +87,8 @@
             }
     }
 
-    @Composable
-    override fun Page(arguments: Bundle?) {
-        RegularScaffold(title = TITLE) {
-            for (entry in buildEntry(arguments)) {
-                entry.UiLayout()
-            }
-        }
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
     }
 }
 
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt
index b991f59..22da99c 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt
@@ -34,7 +34,6 @@
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
 import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
-import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 import kotlinx.coroutines.delay
 
 private const val TITLE = "Sample TwoTargetSwitchPreference"
@@ -88,13 +87,8 @@
             }
     }
 
-    @Composable
-    override fun Page(arguments: Bundle?) {
-        RegularScaffold(title = TITLE) {
-            for (entry in buildEntry(arguments)) {
-                entry.UiLayout()
-            }
-        }
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
     }
 }
 
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt
index a4713b9..d87cbe8 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt
@@ -48,41 +48,40 @@
             }
     }
 
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+
     @Composable
     override fun Page(arguments: Bundle?) {
-        CategoryPage()
-    }
-}
-
-@Composable
-private fun CategoryPage() {
-    RegularScaffold(title = TITLE) {
-        CategoryTitle("Category A")
-        Preference(remember {
-            object : PreferenceModel {
-                override val title = "Preference 1"
-                override val summary = stateOf("Summary 1")
-            }
-        })
-        Preference(remember {
-            object : PreferenceModel {
-                override val title = "Preference 2"
-                override val summary = stateOf("Summary 2")
-            }
-        })
-        Category("Category B") {
+        RegularScaffold(title = getTitle(arguments)) {
+            CategoryTitle("Category A")
             Preference(remember {
                 object : PreferenceModel {
-                    override val title = "Preference 3"
-                    override val summary = stateOf("Summary 3")
+                    override val title = "Preference 1"
+                    override val summary = stateOf("Summary 1")
                 }
             })
             Preference(remember {
                 object : PreferenceModel {
-                    override val title = "Preference 4"
-                    override val summary = stateOf("Summary 4")
+                    override val title = "Preference 2"
+                    override val summary = stateOf("Summary 2")
                 }
             })
+            Category("Category B") {
+                Preference(remember {
+                    object : PreferenceModel {
+                        override val title = "Preference 3"
+                        override val summary = stateOf("Summary 3")
+                    }
+                })
+                Preference(remember {
+                    object : PreferenceModel {
+                        override val title = "Preference 4"
+                        override val summary = stateOf("Summary 4")
+                    }
+                })
+            }
         }
     }
 }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt
index 03b72d3..ec2f436 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt
@@ -49,9 +49,13 @@
             }
     }
 
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+
     @Composable
     override fun Page(arguments: Bundle?) {
-        RegularScaffold(title = TITLE) {
+        RegularScaffold(title = getTitle(arguments)) {
             val selectedIndex = rememberSaveable { mutableStateOf(0) }
             Spinner(
                 options = (1..3).map { "Option $it" },
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
index 2820ed7..3b159e9 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -59,9 +59,9 @@
     api "androidx.compose.material:material-icons-extended:$jetpack_compose_version"
     api "androidx.compose.runtime:runtime-livedata:$jetpack_compose_version"
     api "androidx.compose.ui:ui-tooling-preview:$jetpack_compose_version"
-    api "androidx.lifecycle:lifecycle-livedata-ktx:2.6.0-alpha02"
+    api "androidx.lifecycle:lifecycle-livedata-ktx:2.6.0-alpha03"
     api "androidx.navigation:navigation-compose:2.5.0"
-    api "com.google.android.material:material:1.6.1"
+    api "com.google.android.material:material:1.7.0-alpha03"
     debugApi "androidx.compose.ui:ui-tooling:$jetpack_compose_version"
     implementation "com.airbnb.android:lottie-compose:5.2.0"
 }
diff --git a/packages/SettingsLib/Spa/spa/res/values/themes.xml b/packages/SettingsLib/Spa/spa/res/values/themes.xml
index e0e5fc2..25846ec 100644
--- a/packages/SettingsLib/Spa/spa/res/values/themes.xml
+++ b/packages/SettingsLib/Spa/spa/res/values/themes.xml
@@ -16,12 +16,10 @@
 -->
 <resources>
 
-    <style name="Theme.SpaLib" parent="Theme.Material3.DayNight.NoActionBar">
+    <style name="Theme.SpaLib" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="android:statusBarColor">@android:color/transparent</item>
         <item name="android:navigationBarColor">@android:color/transparent</item>
-    </style>
-
-    <style name="Theme.SpaLib.DayNight">
-        <item name="android:windowLightStatusBar">true</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
     </style>
 </resources>
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index d3efaa7..c3c90ab 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -27,6 +27,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.core.view.WindowCompat
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleEventObserver
 import androidx.navigation.NavGraph.Companion.findStartDestination
@@ -66,8 +67,9 @@
     private val spaEnvironment get() = SpaEnvironmentFactory.instance
 
     override fun onCreate(savedInstanceState: Bundle?) {
-        setTheme(R.style.Theme_SpaLib_DayNight)
+        setTheme(R.style.Theme_SpaLib)
         super.onCreate(savedInstanceState)
+        WindowCompat.setDecorFitsSystemWindows(window, false)
         spaEnvironment.logger.message(TAG, "onCreate", category = LogCategory.FRAMEWORK)
 
         setContent {
@@ -83,35 +85,19 @@
         val navController = rememberNavController()
         val nullPage = SettingsPage.createNull()
         CompositionLocalProvider(navController.localNavController()) {
-            NavHost(navController, nullPage.sppName) {
+            NavHost(
+                navController = navController,
+                startDestination = nullPage.sppName,
+            ) {
                 composable(nullPage.sppName) {}
                 for (spp in sppRepository.getAllProviders()) {
                     composable(
                         route = spp.name + spp.parameter.navRoute(),
                         arguments = spp.parameter,
                     ) { navBackStackEntry ->
-                        val lifecycleOwner = LocalLifecycleOwner.current
-                        val sp = remember(navBackStackEntry.arguments) {
+                        PageLogger(remember(navBackStackEntry.arguments) {
                             spp.createSettingsPage(arguments = navBackStackEntry.arguments)
-                        }
-
-                        DisposableEffect(lifecycleOwner) {
-                            val observer = LifecycleEventObserver { _, event ->
-                                if (event == Lifecycle.Event.ON_START) {
-                                    sp.enterPage()
-                                } else if (event == Lifecycle.Event.ON_STOP) {
-                                    sp.leavePage()
-                                }
-                            }
-
-                            // Add the observer to the lifecycle
-                            lifecycleOwner.lifecycle.addObserver(observer)
-
-                            // When the effect leaves the Composition, remove the observer
-                            onDispose {
-                                lifecycleOwner.lifecycle.removeObserver(observer)
-                            }
-                        }
+                        })
 
                         spp.Page(navBackStackEntry.arguments)
                     }
@@ -122,6 +108,28 @@
     }
 
     @Composable
+    private fun PageLogger(settingsPage: SettingsPage) {
+        val lifecycleOwner = LocalLifecycleOwner.current
+        DisposableEffect(lifecycleOwner) {
+            val observer = LifecycleEventObserver { _, event ->
+                if (event == Lifecycle.Event.ON_START) {
+                    settingsPage.enterPage()
+                } else if (event == Lifecycle.Event.ON_STOP) {
+                    settingsPage.leavePage()
+                }
+            }
+
+            // Add the observer to the lifecycle
+            lifecycleOwner.lifecycle.addObserver(observer)
+
+            // When the effect leaves the Composition, remove the observer
+            onDispose {
+                lifecycleOwner.lifecycle.removeObserver(observer)
+            }
+        }
+    }
+
+    @Composable
     private fun InitialDestinationNavigator() {
         val sppRepository by spaEnvironment.pageProviderRepository
         val destinationNavigated = rememberSaveable { mutableStateOf(false) }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
index f8963b2..151b50cd 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
@@ -19,6 +19,7 @@
 import android.os.Bundle
 import androidx.compose.runtime.Composable
 import androidx.navigation.NamedNavArgument
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 
 /**
  * An SettingsPageProvider which is used to create Settings page instances.
@@ -36,13 +37,19 @@
     val parameter: List<NamedNavArgument>
         get() = emptyList()
 
-    /** The [Composable] used to render this page. */
-    @Composable
-    fun Page(arguments: Bundle?)
+    fun getTitle(arguments: Bundle?): String = displayName ?: name
 
     fun buildEntry(arguments: Bundle?): List<SettingsEntry> = emptyList()
 
-    fun getTitle(arguments: Bundle?): String = displayName ?: name
+    /** The [Composable] used to render this page. */
+    @Composable
+    fun Page(arguments: Bundle?) {
+        RegularScaffold(title = getTitle(arguments)) {
+            for (entry in buildEntry(arguments)) {
+                entry.UiLayout()
+            }
+        }
+    }
 }
 
 fun SettingsPageProvider.createSettingsPage(arguments: Bundle? = null): SettingsPage {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
index 5baee4f..b831043 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -17,7 +17,10 @@
 package com.android.settingslib.spa.framework.common
 
 import android.app.Activity
+import android.content.Context
 import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
 
 private const val TAG = "SpaEnvironment"
 
@@ -29,6 +32,20 @@
         Log.d(TAG, "reset")
     }
 
+    @Composable
+    fun resetForPreview() {
+        val context = LocalContext.current
+        spaEnvironment = object : SpaEnvironment(context) {
+            override val pageProviderRepository = lazy {
+                SettingsPageProviderRepository(
+                    allPageProviders = emptyList(),
+                    rootPages = emptyList()
+                )
+            }
+        }
+        Log.d(TAG, "resetForPreview")
+    }
+
     val instance: SpaEnvironment
         get() {
             if (spaEnvironment == null)
@@ -37,11 +54,14 @@
         }
 }
 
-abstract class SpaEnvironment {
+abstract class SpaEnvironment(context: Context) {
     abstract val pageProviderRepository: Lazy<SettingsPageProviderRepository>
 
     val entryRepository = lazy { SettingsEntryRepository(pageProviderRepository.value) }
 
+    // In Robolectric test, applicationContext is not available. Use context as fallback.
+    val appContext: Context = context.applicationContext ?: context
+
     open val browseActivityClass: Class<out Activity>? = null
 
     open val entryProviderAuthorities: String? = null
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/PaddingValuesExt.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/PaddingValuesExt.kt
new file mode 100644
index 0000000..18335ff
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/PaddingValuesExt.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.settingslib.spa.framework.compose
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+
+internal fun PaddingValues.horizontalValues(): PaddingValues = HorizontalPaddingValues(this)
+
+internal fun PaddingValues.verticalValues(): PaddingValues = VerticalPaddingValues(this)
+
+private class HorizontalPaddingValues(private val paddingValues: PaddingValues) : PaddingValues {
+    override fun calculateLeftPadding(layoutDirection: LayoutDirection) =
+        paddingValues.calculateLeftPadding(layoutDirection)
+
+    override fun calculateTopPadding(): Dp = 0.dp
+
+    override fun calculateRightPadding(layoutDirection: LayoutDirection) =
+        paddingValues.calculateRightPadding(layoutDirection)
+
+    override fun calculateBottomPadding() = 0.dp
+}
+
+private class VerticalPaddingValues(private val paddingValues: PaddingValues) : PaddingValues {
+    override fun calculateLeftPadding(layoutDirection: LayoutDirection) = 0.dp
+
+    override fun calculateTopPadding(): Dp = paddingValues.calculateTopPadding()
+
+    override fun calculateRightPadding(layoutDirection: LayoutDirection) = 0.dp
+
+    override fun calculateBottomPadding() = paddingValues.calculateBottomPadding()
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugActivity.kt
index 9eaa88a..26491d5 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugActivity.kt
@@ -62,7 +62,7 @@
     private val spaEnvironment get() = SpaEnvironmentFactory.instance
 
     override fun onCreate(savedInstanceState: Bundle?) {
-        setTheme(R.style.Theme_SpaLib_DayNight)
+        setTheme(R.style.Theme_SpaLib)
         super.onCreate(savedInstanceState)
         spaEnvironment.logger.message(TAG, "onCreate", category = LogCategory.FRAMEWORK)
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/HomeScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/HomeScaffold.kt
index eb20ac5..711c8a7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/HomeScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/HomeScaffold.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.systemBarsPadding
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.MaterialTheme
@@ -34,6 +35,7 @@
         Modifier
             .fillMaxSize()
             .background(color = MaterialTheme.colorScheme.background)
+            .systemBarsPadding()
             .verticalScroll(rememberScrollState()),
     ) {
         Text(
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/RegularScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/RegularScaffold.kt
index 9a17b2a..d17a8dc 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/RegularScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/RegularScaffold.kt
@@ -19,7 +19,7 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.Scaffold
@@ -27,6 +27,8 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.tooling.preview.Preview
 import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
 
 /**
  * A [Scaffold] which content is scrollable and wrapped in a [Column].
@@ -42,8 +44,9 @@
 ) {
     SettingsScaffold(title, actions) { paddingValues ->
         Column(Modifier.verticalScroll(rememberScrollState())) {
-            Spacer(Modifier.padding(paddingValues))
+            Spacer(Modifier.height(paddingValues.calculateTopPadding()))
             content()
+            Spacer(Modifier.height(paddingValues.calculateBottomPadding()))
         }
     }
 }
@@ -52,6 +55,13 @@
 @Composable
 private fun RegularScaffoldPreview() {
     SettingsTheme {
-        RegularScaffold(title = "Display") {}
+        RegularScaffold(title = "Display") {
+            Preference(object : PreferenceModel {
+                override val title = "Item 1"
+            })
+            Preference(object : PreferenceModel {
+                override val title = "Item 2"
+            })
+        }
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
index 4f83ad6..efc623a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalMaterial3Api::class)
-
 package com.android.settingslib.spa.widget.scaffold
 
 import androidx.activity.compose.BackHandler
 import androidx.appcompat.R
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -31,10 +30,13 @@
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextField
 import androidx.compose.material3.TextFieldDefaults
 import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.State
@@ -48,45 +50,57 @@
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.settingslib.spa.framework.compose.hideKeyboardAction
+import com.android.settingslib.spa.framework.compose.horizontalValues
 import com.android.settingslib.spa.framework.theme.SettingsOpacity
 import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
 
 /**
  * A [Scaffold] which content is can be full screen, and with a search feature built-in.
  */
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun SearchScaffold(
     title: String,
     actions: @Composable RowScope.() -> Unit = {},
-    content: @Composable (searchQuery: State<String>) -> Unit,
+    content: @Composable (bottomPadding: Dp, searchQuery: State<String>) -> Unit,
 ) {
     val viewModel: SearchScaffoldViewModel = viewModel()
 
+    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
     Scaffold(
+        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
         topBar = {
             SearchableTopAppBar(
                 title = title,
                 actions = actions,
+                scrollBehavior = scrollBehavior,
                 searchQuery = viewModel.searchQuery,
             ) { viewModel.searchQuery = it }
         },
     ) { paddingValues ->
         Box(
             Modifier
-                .padding(paddingValues)
-                .fillMaxSize()
+                .padding(paddingValues.horizontalValues())
+                .padding(top = paddingValues.calculateTopPadding())
+                .fillMaxSize(),
         ) {
-            val searchQuery = remember {
-                derivedStateOf { viewModel.searchQuery?.text ?: "" }
-            }
-            content(searchQuery)
+            content(
+                bottomPadding = paddingValues.calculateBottomPadding(),
+                searchQuery = remember {
+                    derivedStateOf { viewModel.searchQuery?.text ?: "" }
+                },
+            )
         }
     }
 }
@@ -95,10 +109,12 @@
     var searchQuery: TextFieldValue? by mutableStateOf(null)
 }
 
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 private fun SearchableTopAppBar(
     title: String,
     actions: @Composable RowScope.() -> Unit,
+    scrollBehavior: TopAppBarScrollBehavior,
     searchQuery: TextFieldValue?,
     onSearchQueryChange: (TextFieldValue?) -> Unit,
 ) {
@@ -110,13 +126,17 @@
             actions = actions,
         )
     } else {
-        SettingsTopAppBar(title) {
-            SearchAction { onSearchQueryChange(TextFieldValue()) }
+        SettingsTopAppBar(title, scrollBehavior) {
+            SearchAction {
+                scrollBehavior.collapse()
+                onSearchQueryChange(TextFieldValue())
+            }
             actions()
         }
     }
 }
 
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 private fun SearchTopAppBar(
     query: TextFieldValue,
@@ -124,21 +144,24 @@
     onClose: () -> Unit,
     actions: @Composable RowScope.() -> Unit = {},
 ) {
-    TopAppBar(
-        title = { SearchBox(query, onQueryChange) },
-        modifier = Modifier.statusBarsPadding(),
-        navigationIcon = { CollapseAction(onClose) },
-        actions = {
-            if (query.text.isNotEmpty()) {
-                ClearAction { onQueryChange(TextFieldValue()) }
-            }
-            actions()
-        },
-        colors = settingsTopAppBarColors(),
-    )
+    Surface(color = SettingsTheme.colorScheme.surfaceHeader) {
+        TopAppBar(
+            title = { SearchBox(query, onQueryChange) },
+            modifier = Modifier.statusBarsPadding(),
+            navigationIcon = { CollapseAction(onClose) },
+            actions = {
+                if (query.text.isNotEmpty()) {
+                    ClearAction { onQueryChange(TextFieldValue()) }
+                }
+                actions()
+            },
+            colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = Color.Transparent),
+        )
+    }
     BackHandler { onClose() }
 }
 
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 private fun SearchBox(query: TextFieldValue, onQueryChange: (TextFieldValue) -> Unit) {
     val focusRequester = remember { FocusRequester() }
@@ -184,6 +207,15 @@
 @Composable
 private fun SearchScaffoldPreview() {
     SettingsTheme {
-        SearchScaffold(title = "App notifications") {}
+        SearchScaffold(title = "App notifications") { _, _ ->
+            Column {
+                Preference(object : PreferenceModel {
+                    override val title = "Item 1"
+                })
+                Preference(object : PreferenceModel {
+                    override val title = "Item 2"
+                })
+            }
+        }
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
index 3bc3dd7..f4e504a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
@@ -16,13 +16,23 @@
 
 package com.android.settingslib.spa.widget.scaffold
 
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Scaffold
+import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.compose.horizontalValues
+import com.android.settingslib.spa.framework.compose.verticalValues
 import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
 
 /**
  * A [Scaffold] which content is can be full screen when needed.
@@ -34,16 +44,30 @@
     actions: @Composable RowScope.() -> Unit = {},
     content: @Composable (PaddingValues) -> Unit,
 ) {
+    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
     Scaffold(
-        topBar = { SettingsTopAppBar(title, actions) },
-        content = content,
-    )
+        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+        topBar = { SettingsTopAppBar(title, scrollBehavior, actions) },
+    ) { paddingValues ->
+        Box(Modifier.padding(paddingValues.horizontalValues())) {
+            content(paddingValues.verticalValues())
+        }
+    }
 }
 
 @Preview
 @Composable
 private fun SettingsScaffoldPreview() {
     SettingsTheme {
-        SettingsScaffold(title = "Display") {}
+        SettingsScaffold(title = "Display") { paddingValues ->
+            Column(Modifier.padding(paddingValues)) {
+                Preference(object : PreferenceModel {
+                    override val title = "Item 1"
+                })
+                Preference(object : PreferenceModel {
+                    override val title = "Item 2"
+                })
+            }
+        }
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt
index 9353520..f7cb035 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt
@@ -17,41 +17,70 @@
 package com.android.settingslib.spa.widget.scaffold
 
 import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.asPaddingValues
+import androidx.compose.foundation.layout.navigationBars
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.LargeTopAppBar
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBar
 import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.text.style.TextOverflow
+import com.android.settingslib.spa.framework.compose.horizontalValues
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.theme.rememberSettingsTypography
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 internal fun SettingsTopAppBar(
     title: String,
+    scrollBehavior: TopAppBarScrollBehavior,
     actions: @Composable RowScope.() -> Unit,
 ) {
-    TopAppBar(
-        title = {
-            Text(
-                text = title,
-                modifier = Modifier.padding(SettingsDimension.itemPaddingAround),
-                overflow = TextOverflow.Ellipsis,
-                maxLines = 1,
-            )
-        },
-        navigationIcon = { NavigateBack() },
-        actions = actions,
-        colors = settingsTopAppBarColors(),
+    val colorScheme = MaterialTheme.colorScheme
+    // TODO: Remove MaterialTheme() after top app bar color fixed in AndroidX.
+    MaterialTheme(
+        colorScheme = remember { colorScheme.copy(surface = colorScheme.background) },
+        typography = rememberSettingsTypography(),
+    ) {
+        LargeTopAppBar(
+            title = { Title(title) },
+            navigationIcon = { NavigateBack() },
+            actions = actions,
+            colors = largeTopAppBarColors(),
+            scrollBehavior = scrollBehavior,
+        )
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+internal fun TopAppBarScrollBehavior.collapse() {
+    with(state) {
+        heightOffset = heightOffsetLimit
+    }
+}
+
+@Composable
+private fun Title(title: String) {
+    Text(
+        text = title,
+        modifier = Modifier
+            .padding(WindowInsets.navigationBars.asPaddingValues().horizontalValues())
+            .padding(SettingsDimension.itemPaddingAround),
+        overflow = TextOverflow.Ellipsis,
+        maxLines = 1,
     )
 }
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
-internal fun settingsTopAppBarColors() = TopAppBarDefaults.smallTopAppBarColors(
-    containerColor = SettingsTheme.colorScheme.surfaceHeader,
+private fun largeTopAppBarColors() = TopAppBarDefaults.largeTopAppBarColors(
+    containerColor = MaterialTheme.colorScheme.background,
     scrolledContainerColor = SettingsTheme.colorScheme.surfaceHeader,
 )
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/util/EntryHighlight.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/util/EntryHighlight.kt
index 652e54d..e26bdf7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/util/EntryHighlight.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/util/EntryHighlight.kt
@@ -16,21 +16,43 @@
 
 package com.android.settingslib.spa.widget.util
 
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.RepeatMode
+import androidx.compose.animation.core.repeatable
+import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
 import com.android.settingslib.spa.framework.common.LocalEntryDataProvider
+import com.android.settingslib.spa.framework.theme.SettingsTheme
 
 @Composable
 internal fun EntryHighlight(UiLayoutFn: @Composable () -> Unit) {
     val entryData = LocalEntryDataProvider.current
-    val isHighlighted = rememberSaveable { entryData.isHighlighted }
-    val backgroundColor =
-        if (isHighlighted) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent
+    val entryIsHighlighted = rememberSaveable { entryData.isHighlighted }
+    var localHighlighted by rememberSaveable { mutableStateOf(false) }
+    SideEffect {
+        localHighlighted = entryIsHighlighted
+    }
+
+    val backgroundColor by animateColorAsState(
+        targetValue = when {
+            localHighlighted -> MaterialTheme.colorScheme.surfaceVariant
+            else -> SettingsTheme.colorScheme.background
+        },
+        animationSpec = repeatable(
+            iterations = 3,
+            animation = tween(durationMillis = 500),
+            repeatMode = RepeatMode.Restart
+        )
+    )
     Box(modifier = Modifier.background(color = backgroundColor)) {
         UiLayoutFn()
     }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/ParameterTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/ParameterTest.kt
new file mode 100644
index 0000000..21ff085
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/ParameterTest.kt
@@ -0,0 +1,182 @@
+/*
+ * 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.settingslib.spa.framework.util
+
+import androidx.core.os.bundleOf
+import androidx.navigation.NamedNavArgument
+import androidx.navigation.NavType
+import androidx.navigation.navArgument
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class ParameterTest {
+    @Test
+    fun navRouteTest() {
+        val navArguments = listOf(
+            navArgument("string_param") { type = NavType.StringType },
+            navArgument("int_param") { type = NavType.IntType },
+        )
+
+        val route = navArguments.navRoute()
+        assertThat(route).isEqualTo("/{string_param}/{int_param}")
+    }
+
+    @Test
+    fun navLinkTest() {
+        val navArguments = listOf(
+            navArgument("string_param") { type = NavType.StringType },
+            navArgument("int_param") { type = NavType.IntType },
+        )
+
+        val unsetAllLink = navArguments.navLink()
+        assertThat(unsetAllLink).isEqualTo("/[unset]/[unset]")
+
+        val setAllLink = navArguments.navLink(
+            bundleOf(
+                "string_param" to "myStr",
+                "int_param" to 10,
+            )
+        )
+        assertThat(setAllLink).isEqualTo("/myStr/10")
+
+        val setUnknownLink = navArguments.navLink(
+            bundleOf(
+                "string_param" to "myStr",
+                "int_param" to 10,
+                "unknown_param" to "unknown",
+            )
+        )
+        assertThat(setUnknownLink).isEqualTo("/myStr/10")
+
+        val setWrongTypeLink = navArguments.navLink(
+            bundleOf(
+                "string_param" to "myStr",
+                "int_param" to "wrongStr",
+            )
+        )
+        assertThat(setWrongTypeLink).isEqualTo("/myStr/0")
+    }
+
+    @Test
+    fun normalizeTest() {
+        val emptyArguments = emptyList<NamedNavArgument>()
+        assertThat(emptyArguments.normalize()).isNull()
+
+        val navArguments = listOf(
+            navArgument("string_param") { type = NavType.StringType },
+            navArgument("int_param") { type = NavType.IntType },
+            navArgument("rt_param") { type = NavType.StringType },
+        )
+
+        val emptyParam = navArguments.normalize()
+        assertThat(emptyParam).isNotNull()
+        assertThat(emptyParam.toString()).isEqualTo(
+            "Bundle[{rt_param=null, unset_string_param=null, unset_int_param=null}]"
+        )
+
+        val setParialParam = navArguments.normalize(
+            bundleOf(
+                "string_param" to "myStr",
+                "rt_param" to "rtStr",
+            )
+        )
+        assertThat(setParialParam).isNotNull()
+        assertThat(setParialParam.toString()).isEqualTo(
+            "Bundle[{rt_param=null, string_param=myStr, unset_int_param=null}]"
+        )
+
+        val setAllParam = navArguments.normalize(
+            bundleOf(
+                "string_param" to "myStr",
+                "int_param" to 10,
+                "rt_param" to "rtStr",
+            )
+        )
+        assertThat(setAllParam).isNotNull()
+        assertThat(setAllParam.toString()).isEqualTo(
+            "Bundle[{rt_param=null, int_param=10, string_param=myStr}]"
+        )
+    }
+
+    @Test
+    fun getArgTest() {
+        val navArguments = listOf(
+            navArgument("string_param") { type = NavType.StringType },
+            navArgument("int_param") { type = NavType.IntType },
+        )
+
+        assertThat(
+            navArguments.getStringArg(
+                "string_param", bundleOf(
+                    "string_param" to "myStr",
+                )
+            )
+        ).isEqualTo("myStr")
+
+        assertThat(
+            navArguments.getStringArg(
+                "string_param", bundleOf(
+                    "string_param" to 10,
+                )
+            )
+        ).isNull()
+
+        assertThat(
+            navArguments.getStringArg(
+                "unknown_param", bundleOf(
+                    "string_param" to "myStr",
+                )
+            )
+        ).isNull()
+
+        assertThat(navArguments.getStringArg("string_param")).isNull()
+
+        assertThat(
+            navArguments.getIntArg(
+                "int_param", bundleOf(
+                    "int_param" to 10,
+                )
+            )
+        ).isEqualTo(10)
+
+        assertThat(
+            navArguments.getIntArg(
+                "int_param", bundleOf(
+                    "int_param" to "10",
+                )
+            )
+        ).isEqualTo(0)
+
+        assertThat(
+            navArguments.getIntArg(
+                "unknown_param", bundleOf(
+                    "int_param" to 10,
+                )
+            )
+        ).isNull()
+
+        assertThat(navArguments.getIntArg("int_param")).isNull()
+    }
+
+    @Test
+    fun isRuntimeParamTest() {
+        val regularParam = navArgument("regular_param") { type = NavType.StringType }
+        val rtParam = navArgument("rt_param") { type = NavType.StringType }
+        assertThat(regularParam.isRuntimeParam()).isFalse()
+        assertThat(rtParam.isRuntimeParam()).isTrue()
+    }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/RegularScaffoldTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/RegularScaffoldTest.kt
new file mode 100644
index 0000000..1964c43
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/RegularScaffoldTest.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.settingslib.spa.widget.scaffold
+
+import androidx.compose.material3.Text
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RegularScaffoldTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun regularScaffold_titleIsDisplayed() {
+        composeTestRule.setContent {
+            RegularScaffold(title = TITLE) {
+                Text(text = "AAA")
+                Text(text = "BBB")
+            }
+        }
+
+        composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+    }
+
+    @Test
+    fun regularScaffold_itemsAreDisplayed() {
+        composeTestRule.setContent {
+            RegularScaffold(title = TITLE) {
+                Text(text = "AAA")
+                Text(text = "BBB")
+            }
+        }
+
+        composeTestRule.onNodeWithText("AAA").assertIsDisplayed()
+        composeTestRule.onNodeWithText("BBB").assertIsDisplayed()
+    }
+
+    private companion object {
+        const val TITLE = "title"
+    }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SearchScaffoldTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SearchScaffoldTest.kt
index ec3379d..c3e1d54 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SearchScaffoldTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SearchScaffoldTest.kt
@@ -43,7 +43,7 @@
     @Test
     fun initialState_titleIsDisplayed() {
         composeTestRule.setContent {
-            SearchScaffold(title = TITLE) {}
+            SearchScaffold(title = TITLE) { _, _ -> }
         }
 
         composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
@@ -116,7 +116,7 @@
     private fun setContent(): State<String> {
         lateinit var actualSearchQuery: State<String>
         composeTestRule.setContent {
-            SearchScaffold(title = TITLE) { searchQuery ->
+            SearchScaffold(title = TITLE) { _, searchQuery ->
                 SideEffect {
                     actualSearchQuery = searchQuery
                 }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerTest.kt
similarity index 89%
rename from packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt
rename to packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerTest.kt
index 0c84eac..0c745d5 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.settingslib.spa.widget.scaffold
 
-import androidx.compose.runtime.Composable
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.assertIsNotSelected
@@ -31,15 +30,13 @@
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
-class SettingsPagerKtTest {
+class SettingsPagerTest {
     @get:Rule
     val composeTestRule = createComposeRule()
 
     @Test
     fun twoPage_initialState() {
-        composeTestRule.setContent {
-            TestTwoPage()
-        }
+        setTwoPagesContent()
 
         composeTestRule.onNodeWithText("Personal").assertIsSelected()
         composeTestRule.onNodeWithText("Page 0").assertIsDisplayed()
@@ -49,9 +46,7 @@
 
     @Test
     fun twoPage_afterSwitch() {
-        composeTestRule.setContent {
-            TestTwoPage()
-        }
+        setTwoPagesContent()
 
         composeTestRule.onNodeWithText("Work").performClick()
 
@@ -73,11 +68,12 @@
         composeTestRule.onNodeWithText("Page 0").assertIsDisplayed()
         composeTestRule.onNodeWithText("Page 1").assertDoesNotExist()
     }
-}
 
-@Composable
-private fun TestTwoPage() {
-    SettingsPager(listOf("Personal", "Work")) {
-        SettingsTitle(title = "Page $it")
+    private fun setTwoPagesContent() {
+        composeTestRule.setContent {
+            SettingsPager(listOf("Personal", "Work")) {
+                SettingsTitle(title = "Page $it")
+            }
+        }
     }
 }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffoldTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffoldTest.kt
new file mode 100644
index 0000000..f042404
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffoldTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.settingslib.spa.widget.scaffold
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.material3.Text
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsScaffoldTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun settingsScaffold_titleIsDisplayed() {
+        composeTestRule.setContent {
+            SettingsScaffold(title = TITLE) {
+                Text(text = "AAA")
+                Text(text = "BBB")
+            }
+        }
+
+        composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+    }
+
+    @Test
+    fun settingsScaffold_itemsAreDisplayed() {
+        composeTestRule.setContent {
+            SettingsScaffold(title = TITLE) {
+                Text(text = "AAA")
+                Text(text = "BBB")
+            }
+        }
+
+        composeTestRule.onNodeWithText("AAA").assertIsDisplayed()
+        composeTestRule.onNodeWithText("BBB").assertIsDisplayed()
+    }
+
+    @Test
+    fun settingsScaffold_noHorizontalPadding() {
+        lateinit var actualPaddingValues: PaddingValues
+
+        composeTestRule.setContent {
+            SettingsScaffold(title = TITLE) { paddingValues ->
+                SideEffect {
+                    actualPaddingValues = paddingValues
+                }
+            }
+        }
+
+        assertThat(actualPaddingValues.calculateLeftPadding(LayoutDirection.Ltr)).isEqualTo(0.dp)
+        assertThat(actualPaddingValues.calculateLeftPadding(LayoutDirection.Rtl)).isEqualTo(0.dp)
+        assertThat(actualPaddingValues.calculateRightPadding(LayoutDirection.Ltr)).isEqualTo(0.dp)
+        assertThat(actualPaddingValues.calculateRightPadding(LayoutDirection.Rtl)).isEqualTo(0.dp)
+    }
+
+    private companion object {
+        const val TITLE = "title"
+    }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt
index fd723dd..bb1cd6e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt
@@ -22,6 +22,7 @@
 import android.app.usage.StorageStatsManager
 import android.apphibernation.AppHibernationManager
 import android.content.Context
+import android.content.pm.CrossProfileApps
 import android.content.pm.verify.domain.DomainVerificationManager
 import android.os.UserHandle
 import android.os.UserManager
@@ -36,6 +37,9 @@
 /** The [AppOpsManager] instance. */
 val Context.appOpsManager get() = getSystemService(AppOpsManager::class.java)!!
 
+/** The [CrossProfileApps] instance. */
+val Context.crossProfileApps get() = getSystemService(CrossProfileApps::class.java)!!
+
 /** The [DevicePolicyManager] instance. */
 val Context.devicePolicyManager get() = getSystemService(DevicePolicyManager::class.java)!!
 
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt
index c1ac5d4..8954d22 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt
@@ -35,6 +35,9 @@
 /** Checks whether a flag is associated with the application. */
 fun ApplicationInfo.hasFlag(flag: Int): Boolean = (flags and flag) > 0
 
+/** Checks whether the application is currently installed. */
+val ApplicationInfo.installed: Boolean get() = hasFlag(ApplicationInfo.FLAG_INSTALLED)
+
 /** Checks whether the application is disabled until used. */
 val ApplicationInfo.isDisabledUntilUsed: Boolean
     get() = enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagerExt.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagerExt.kt
new file mode 100644
index 0000000..2b2f11c
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagerExt.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.settingslib.spaprivileged.model.app
+
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ResolveInfoFlags
+
+/**
+ * Checks if a package is system module.
+ */
+fun PackageManager.isSystemModule(packageName: String): Boolean = try {
+    getModuleInfo(packageName, 0)
+    true
+} catch (_: PackageManager.NameNotFoundException) {
+    // Expected, not system module
+    false
+}
+
+/**
+ * Resolves the activity to start for a given application and action.
+ */
+fun PackageManager.resolveActionForApp(
+    app: ApplicationInfo,
+    action: String,
+    flags: Int = 0,
+): ActivityInfo? {
+    val intent = Intent(action).apply {
+        `package` = app.packageName
+    }
+    return resolveActivityAsUser(intent, ResolveInfoFlags.of(flags.toLong()), app.userId)
+        ?.activityInfo
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index 408b9df..3cd8378 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -25,12 +25,12 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.Dp
 import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.settingslib.spa.framework.compose.LogCompositions
 import com.android.settingslib.spa.framework.compose.TimeMeasurer.Companion.rememberTimeMeasurer
 import com.android.settingslib.spa.framework.compose.rememberLazyListStateAndHideKeyboardWhenStartScroll
 import com.android.settingslib.spa.framework.compose.toState
-import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.widget.ui.PlaceholderTitle
 import com.android.settingslib.spaprivileged.R
 import com.android.settingslib.spaprivileged.model.app.AppListConfig
@@ -55,10 +55,11 @@
     option: State<Int>,
     searchQuery: State<String>,
     appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
+    bottomPadding: Dp,
 ) {
     LogCompositions(TAG, appListConfig.userId.toString())
     val appListData = loadAppEntries(appListConfig, listModel, showSystem, option, searchQuery)
-    AppListWidget(appListData, listModel, appItem)
+    AppListWidget(appListData, listModel, appItem, bottomPadding)
 }
 
 @Composable
@@ -66,6 +67,7 @@
     appListData: State<AppListData<T>?>,
     listModel: AppListModel<T>,
     appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
+    bottomPadding: Dp,
 ) {
     val timeMeasurer = rememberTimeMeasurer(TAG)
     appListData.value?.let { (list, option) ->
@@ -77,7 +79,7 @@
         LazyColumn(
             modifier = Modifier.fillMaxSize(),
             state = rememberLazyListStateAndHideKeyboardWhenStartScroll(),
-            contentPadding = PaddingValues(bottom = SettingsDimension.itemPaddingVertical),
+            contentPadding = PaddingValues(bottom = bottomPadding),
         ) {
             items(count = list.size, key = { option to list[it].record.app.packageName }) {
                 val appEntry = list[it]
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
index 99376b0..2953367 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
@@ -52,7 +52,7 @@
         actions = {
             ShowSystemAction(showSystem.value) { showSystem.value = it }
         },
-    ) { searchQuery ->
+    ) { bottomPadding, searchQuery ->
         WorkProfilePager(primaryUserOnly) { userInfo ->
             Column(Modifier.fillMaxSize()) {
                 val options = remember { listModel.getSpinnerOptions() }
@@ -68,6 +68,7 @@
                     option = selectedOption,
                     searchQuery = searchQuery,
                     appItem = appItem,
+                    bottomPadding = bottomPadding,
                 )
             }
         }
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/PackageManagerExtTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/PackageManagerExtTest.kt
new file mode 100644
index 0000000..4207490
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/PackageManagerExtTest.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.settingslib.spaprivileged.model.app
+
+import android.content.ComponentName
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.ModuleInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
+import android.content.pm.PackageManager.ResolveInfoFlags
+import android.content.pm.ResolveInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class PackageManagerExtTest {
+    @JvmField
+    @Rule
+    val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @Mock
+    private lateinit var packageManager: PackageManager
+
+    private fun mockResolveActivityAsUser(resolveInfo: ResolveInfo?) {
+        whenever(
+            packageManager.resolveActivityAsUser(any(), any<ResolveInfoFlags>(), eq(APP.userId))
+        ).thenReturn(resolveInfo)
+    }
+
+    @Test
+    fun isSystemModule_whenSystemModule_returnTrue() {
+        whenever(packageManager.getModuleInfo(PACKAGE_NAME, 0)).thenReturn(ModuleInfo())
+
+        val isSystemModule = packageManager.isSystemModule(PACKAGE_NAME)
+
+        assertThat(isSystemModule).isTrue()
+    }
+
+    @Test
+    fun isSystemModule_whenNotSystemModule_returnFalse() {
+        whenever(packageManager.getModuleInfo(PACKAGE_NAME, 0)).thenThrow(NameNotFoundException())
+
+        val isSystemModule = packageManager.isSystemModule(PACKAGE_NAME)
+
+        assertThat(isSystemModule).isFalse()
+    }
+
+    @Test
+    fun resolveActionForApp_noResolveInfo() {
+        mockResolveActivityAsUser(null)
+
+        val activityInfo = packageManager.resolveActionForApp(APP, ACTION)
+
+        assertThat(activityInfo).isNull()
+    }
+
+    @Test
+    fun resolveActionForApp_noActivityInfo() {
+        mockResolveActivityAsUser(ResolveInfo())
+
+        val activityInfo = packageManager.resolveActionForApp(APP, ACTION)
+
+        assertThat(activityInfo).isNull()
+    }
+
+    @Test
+    fun resolveActionForApp_hasActivityInfo() {
+        mockResolveActivityAsUser(ResolveInfo().apply {
+            activityInfo = ActivityInfo().apply {
+                packageName = PACKAGE_NAME
+                name = ACTIVITY_NAME
+            }
+        })
+
+        val activityInfo = packageManager.resolveActionForApp(APP, ACTION)!!
+
+        assertThat(activityInfo.componentName).isEqualTo(ComponentName(PACKAGE_NAME, ACTIVITY_NAME))
+    }
+
+    @Test
+    fun resolveActionForApp_withFlags() {
+        packageManager.resolveActionForApp(
+            app = APP,
+            action = ACTION,
+            flags = PackageManager.GET_META_DATA,
+        )
+
+        val flagsCaptor = ArgumentCaptor.forClass(ResolveInfoFlags::class.java)
+        verify(packageManager).resolveActivityAsUser(any(), flagsCaptor.capture(), eq(APP.userId))
+        assertThat(flagsCaptor.value.value).isEqualTo(PackageManager.GET_META_DATA.toLong())
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "package.name"
+        const val ACTIVITY_NAME = "ActivityName"
+        const val ACTION = "action"
+        const val UID = 123
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            uid = UID
+        }
+    }
+}
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 1738e01..efb5eb9 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -548,7 +548,7 @@
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Bu foydalanuvchining umumiy maʼlumotlari topilmadi."</string>
     <string name="shared_data_query_failure_text" msgid="3489828881998773687">"Umumiy maʼlumotlarni yuklashda xatolik yuz berdi. Qayta urining."</string>
     <string name="blob_id_text" msgid="8680078988996308061">"Umumiy maʼlumotlar identifikatori: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
-    <string name="blob_expires_text" msgid="7882727111491739331">"Amal qilish muddati: <xliff:g id="DATE">%s</xliff:g>"</string>
+    <string name="blob_expires_text" msgid="7882727111491739331">"Muddati: <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"Umumiy maʼlumotlarni oʻchirishda xatolik yuz berdi."</string>
     <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"Bu umumiy maʼlumotlar yuzasidan kelgan soʻrov topilmadi. Oʻchirib tashlansinmi?"</string>
     <string name="accessor_info_title" msgid="8289823651512477787">"Umumiy maʼlumotlar bor ilovalar"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/DataServiceUtils.java b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/DataServiceUtils.java
index 03d9f2d..30d3820 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/DataServiceUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/DataServiceUtils.java
@@ -357,5 +357,12 @@
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
         public static final String COLUMN_IS_DEFAULT_SUBSCRIPTION = "isDefaultSubscription";
+
+        /**
+         * The name of the active data subscription state column, see
+         * {@link SubscriptionManager#getActiveDataSubscriptionId()}.
+         */
+        public static final String COLUMN_IS_ACTIVE_DATA_SUBSCRIPTION =
+                "isActiveDataSubscriptionId";
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/MobileNetworkDatabase.java b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/MobileNetworkDatabase.java
index c1ee7ad..ca457b0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/MobileNetworkDatabase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/MobileNetworkDatabase.java
@@ -20,6 +20,7 @@
 import android.util.Log;
 
 import java.util.List;
+import java.util.Objects;
 
 import androidx.lifecycle.LiveData;
 import androidx.room.Database;
@@ -39,17 +40,27 @@
 
     public abstract MobileNetworkInfoDao mMobileNetworkInfoDao();
 
+    private static MobileNetworkDatabase sInstance;
+    private static final Object sLOCK = new Object();
+
+
     /**
      * Create the MobileNetworkDatabase.
      *
      * @param context The context.
      * @return The MobileNetworkDatabase.
      */
-    public static MobileNetworkDatabase createDatabase(Context context) {
-        return Room.inMemoryDatabaseBuilder(context, MobileNetworkDatabase.class)
-                .fallbackToDestructiveMigration()
-                .enableMultiInstanceInvalidation()
-                .build();
+    public static MobileNetworkDatabase getInstance(Context context) {
+        synchronized (sLOCK) {
+            if (Objects.isNull(sInstance)) {
+                Log.d(TAG, "createDatabase.");
+                sInstance = Room.inMemoryDatabaseBuilder(context, MobileNetworkDatabase.class)
+                        .fallbackToDestructiveMigration()
+                        .enableMultiInstanceInvalidation()
+                        .build();
+            }
+        }
+        return sInstance;
     }
 
     /**
@@ -93,7 +104,7 @@
      * Query the subscription info by the subscription ID from the SubscriptionInfoEntity
      * table.
      */
-    public LiveData<SubscriptionInfoEntity> querySubInfoById(String id) {
+    public SubscriptionInfoEntity querySubInfoById(String id) {
         return mSubscriptionInfoDao().querySubInfoById(id);
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java
index 4596637..e835125 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java
@@ -37,7 +37,7 @@
 
     @Query("SELECT * FROM " + DataServiceUtils.SubscriptionInfoData.TABLE_NAME + " WHERE "
             + DataServiceUtils.SubscriptionInfoData.COLUMN_ID + " = :subId")
-    LiveData<SubscriptionInfoEntity> querySubInfoById(String subId);
+    SubscriptionInfoEntity querySubInfoById(String subId);
 
     @Query("SELECT * FROM " + DataServiceUtils.SubscriptionInfoData.TABLE_NAME + " WHERE "
             + DataServiceUtils.SubscriptionInfoData.COLUMN_IS_ACTIVE_SUBSCRIPTION_ID
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java
index 329bd9b..23566f7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java
@@ -42,7 +42,7 @@
             boolean isUsableSubscription, boolean isActiveSubscriptionId,
             boolean isAvailableSubscription, boolean isDefaultVoiceSubscription,
             boolean isDefaultSmsSubscription, boolean isDefaultDataSubscription,
-            boolean isDefaultSubscription) {
+            boolean isDefaultSubscription, boolean isActiveDataSubscriptionId) {
         this.subId = subId;
         this.simSlotIndex = simSlotIndex;
         this.carrierId = carrierId;
@@ -72,6 +72,7 @@
         this.isDefaultSmsSubscription = isDefaultSmsSubscription;
         this.isDefaultDataSubscription = isDefaultDataSubscription;
         this.isDefaultSubscription = isDefaultSubscription;
+        this.isActiveDataSubscriptionId = isActiveDataSubscriptionId;
     }
 
     @PrimaryKey
@@ -165,6 +166,9 @@
     @ColumnInfo(name = DataServiceUtils.SubscriptionInfoData.COLUMN_IS_DEFAULT_SUBSCRIPTION)
     public boolean isDefaultSubscription;
 
+    @ColumnInfo(name = DataServiceUtils.SubscriptionInfoData.COLUMN_IS_ACTIVE_DATA_SUBSCRIPTION)
+    public boolean isActiveDataSubscriptionId;
+
     public int getSubId() {
         return Integer.valueOf(subId);
     }
@@ -213,6 +217,7 @@
         result = 31 * result + Boolean.hashCode(isDefaultSmsSubscription);
         result = 31 * result + Boolean.hashCode(isDefaultDataSubscription);
         result = 31 * result + Boolean.hashCode(isDefaultSubscription);
+        result = 31 * result + Boolean.hashCode(isActiveDataSubscriptionId);
         return result;
     }
 
@@ -254,7 +259,8 @@
                 && isDefaultVoiceSubscription == info.isDefaultVoiceSubscription
                 && isDefaultSmsSubscription == info.isDefaultSmsSubscription
                 && isDefaultDataSubscription == info.isDefaultDataSubscription
-                && isDefaultSubscription == info.isDefaultSubscription;
+                && isDefaultSubscription == info.isDefaultSubscription
+                && isActiveDataSubscriptionId == info.isActiveDataSubscriptionId;
     }
 
     public String toString() {
@@ -317,6 +323,8 @@
                 .append(isDefaultDataSubscription)
                 .append(", isDefaultSubscription = ")
                 .append(isDefaultSubscription)
+                .append(", isActiveDataSubscriptionId = ")
+                .append(isActiveDataSubscriptionId)
                 .append(")}");
         return builder.toString();
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/qrcode/OWNERS b/packages/SettingsLib/src/com/android/settingslib/qrcode/OWNERS
new file mode 100644
index 0000000..61c73fb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/qrcode/OWNERS
@@ -0,0 +1,8 @@
+# Default reviewers for this and subdirectories.
+bonianchen@google.com
+changbetty@google.com
+goldmanj@google.com
+wengsu@google.com
+zoeychen@google.com
+
+# Emergency approvers in case the above are not available
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ccbfac2..fa96a2f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -5533,13 +5533,17 @@
                 }
                 if (currentVersion == 210) {
                     final SettingsState secureSettings = getSecureSettingsLocked(userId);
-                    final int defaultValueVibrateIconEnabled = getContext().getResources()
-                            .getInteger(R.integer.def_statusBarVibrateIconEnabled);
-                    secureSettings.insertSettingOverrideableByRestoreLocked(
-                            Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
-                            String.valueOf(defaultValueVibrateIconEnabled),
-                            null /* tag */, true /* makeDefault */,
-                            SettingsState.SYSTEM_PACKAGE_NAME);
+                    final Setting currentSetting = secureSettings.getSettingLocked(
+                            Secure.STATUS_BAR_SHOW_VIBRATE_ICON);
+                    if (currentSetting.isNull()) {
+                        final int defaultValueVibrateIconEnabled = getContext().getResources()
+                                .getInteger(R.integer.def_statusBarVibrateIconEnabled);
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
+                                Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
+                                String.valueOf(defaultValueVibrateIconEnabled),
+                                null /* tag */, true /* makeDefault */,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
                     currentVersion = 211;
                 }
                 // vXXX: Add new settings above this point.
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
index 38d636d7..95b986f 100644
--- a/packages/SystemUI/docs/device-entry/quickaffordance.md
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -8,7 +8,7 @@
 ### Step 1: create a new quick affordance config
 * Create a new class under the [systemui/keyguard/domain/quickaffordance](../../src/com/android/systemui/keyguard/domain/quickaffordance) directory
 * Please make sure that the class is injected through the Dagger dependency injection system by using the `@Inject` annotation on its main constructor and the `@SysUISingleton` annotation at class level, to make sure only one instance of the class is ever instantiated
-* Have the class implement the [KeyguardQuickAffordanceConfig](../../src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt) interface, notes:
+* Have the class implement the [KeyguardQuickAffordanceConfig](../../src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt) interface, notes:
   * The `state` Flow property must emit `State.Hidden` when the feature is not enabled!
   * It is safe to assume that `onQuickAffordanceClicked` will not be invoked if-and-only-if the previous rule is followed
   * When implementing `onQuickAffordanceClicked`, the implementation can do something or it can ask the framework to start an activity using an `Intent` provided by the implementation
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 0f037e4..06ea381 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -27,8 +27,6 @@
 -packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
 -packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
 -packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
--packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt
--packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt
 -packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt
 -packages/SystemUI/shared/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerManager.kt
 -packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt
@@ -683,8 +681,6 @@
 -packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplingInstanceTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index d64587d..c297149 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -31,7 +31,8 @@
         android:layout_height="wrap_content"
         android:layout_alignParentStart="true"
         android:layout_alignParentTop="true"
-        android:paddingStart="@dimen/clock_padding_start" />
+        android:paddingStart="@dimen/clock_padding_start"
+        android:visibility="invisible" />
     <FrameLayout
         android:id="@+id/lockscreen_clock_view_large"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index d5552f6..1ff549e 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Patroon word vereis nadat toestel herbegin het"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN word vereis nadat toestel herbegin het"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Wagwoord word vereis nadat toestel herbegin het"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Gebruik eerder ’n patroon vir bykomende sekuriteit"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Gebruik eerder ’n PIN vir bykomende sekuriteit"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Gebruik eerder ’n wagwoord vir bykomende sekuriteit"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Toestel is deur administrateur gesluit"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Toestel is handmatig gesluit"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nie herken nie"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Verstek"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Borrel"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analoog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Ontsluit jou toestel om voort te gaan"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 533e5a2..f61c8cf 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"መሣሪያ ዳግም ከጀመረ በኋላ ሥርዓተ ጥለት ያስፈልጋል"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"መሣሪያ ዳግም ከተነሳ በኋላ ፒን ያስፈልጋል"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"መሣሪያ ዳግም ከጀመረ በኋላ የይለፍ ቃል ያስፈልጋል"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ለተጨማሪ ደህንነት በምትኩ ስርዓተ ጥለት ይጠቀሙ"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ለተጨማሪ ደህንነት በምትኩ ፒን ይጠቀሙ"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ለተጨማሪ ደህንነት በምትኩ የይለፍ ቃል ይጠቀሙ"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"መሣሪያ በአስተዳዳሪ ተቆልፏል"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"መሣሪያ በተጠቃሚው ራሱ ተቆልፏል"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"አልታወቀም"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ነባሪ"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"አረፋ"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"አናሎግ"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"ለመቀጠል መሣሪያዎን ይክፈቱ"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 81ce7d3..f3256ba 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"يجب رسم النقش بعد إعادة تشغيل الجهاز"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"يجب إدخال رقم التعريف الشخصي بعد إعادة تشغيل الجهاز"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"يجب إدخال كلمة المرور بعد إعادة تشغيل الجهاز"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"لمزيد من الأمان، استخدِم النقش بدلاً من ذلك."</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"لمزيد من الأمان، أدخِل رقم التعريف الشخصي بدلاً من ذلك."</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"لمزيد من الأمان، أدخِل كلمة المرور بدلاً من ذلك."</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"اختار المشرف قفل الجهاز"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"تم حظر الجهاز يدويًا"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"لم يتم التعرّف عليه."</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"تلقائي"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"فقاعة"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"ساعة تقليدية"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"يجب فتح قفل الجهاز للمتابعة"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index 443f666..f9dc46f 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পাছত আৰ্হি দিয়াটো বাধ্যতামূলক"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পাছত পিন দিয়াটো বাধ্যতামূলক"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পাছত পাছৱৰ্ড দিয়াটো বাধ্যতামূলক"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"অতিৰিক্ত সুৰক্ষাৰ বাবে, ইয়াৰ পৰিৱৰ্তে আৰ্হি ব্যৱহাৰ কৰক"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"অতিৰিক্ত সুৰক্ষাৰ বাবে, ইয়াৰ পৰিৱৰ্তে পিন ব্যৱহাৰ কৰক"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"অতিৰিক্ত সুৰক্ষাৰ বাবে, ইয়াৰ পৰিৱৰ্তে পাছৱৰ্ড ব্যৱহাৰ কৰক"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"প্ৰশাসকে ডিভাইচ লক কৰি ৰাখিছে"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ডিভাইচটো মেনুৱেলভাৱে লক কৰা হৈছিল"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"চিনাক্ত কৰিব পৰা নাই"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ডিফ’ল্ট"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"বাবল"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"এনাল’গ"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"অব্যাহত ৰাখিবলৈ আপোনাৰ ডিভাইচটো আনলক কৰক"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index e125697..65c1c93 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Cihaz yenidən başladıqdan sonra model tələb olunur"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Cihaz yeniden başladıqdan sonra PIN tələb olunur"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Cihaz yeniden başladıqdan sonra parol tələb olunur"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Əlavə təhlükəsizlik üçün modeldən istifadə edin"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Əlavə təhlükəsizlik üçün PIN istifadə edin"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Əlavə təhlükəsizlik üçün paroldan istifadə edin"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Cihaz admin tərəfindən kilidlənib"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Cihaz əl ilə kilidləndi"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Tanınmır"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Defolt"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Qabarcıq"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analoq"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Davam etmək üçün cihazınızın kilidini açın"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index f0d1ef2..cf363df 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Treba da unesete šablon kada se uređaj ponovo pokrene"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Treba da unesete PIN kada se uređaj ponovo pokrene"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Treba da unesete lozinku kada se uređaj ponovo pokrene"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Za dodatnu bezbednost koristite šablon"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Za dodatnu bezbednost koristite PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Za dodatnu bezbednost koristite lozinku"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Administrator je zaključao uređaj"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznat"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Podrazumevani"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Mehurići"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogni"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Otključajte uređaj da biste nastavili"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index e1af3ece..c2dedf30 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Пасля перазапуску прылады патрабуецца ўзор"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Пасля перазапуску прылады патрабуецца PIN-код"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Пасля перазапуску прылады патрабуецца пароль"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"У мэтах дадатковай бяспекі скарыстайце ўзор разблакіроўкі"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"У мэтах дадатковай бяспекі скарыстайце PIN-код"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"У мэтах дадатковай бяспекі скарыстайце пароль"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Прылада заблакіравана адміністратарам"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Прылада была заблакіравана ўручную"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Не распазнана"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Стандартны"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Бурбалкі"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Са стрэлкамі"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Каб працягнуць, разблакіруйце прыладу"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index 0b4417a..546a645 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"След рестартиране на устройството се изисква фигура"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"След рестартиране на устройството се изисква ПИН код"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"След рестартиране на устройството се изисква парола"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"За допълнителна сигурност използвайте фигура вместо това"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"За допълнителна сигурност използвайте ПИН код вместо това"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"За допълнителна сигурност използвайте парола вместо това"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Устройството е заключено от администратора"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Устройството бе заключено ръчно"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Не е разпознато"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Стандартен"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Балонен"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Аналогов"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Отключете устройството си, за да продължите"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 4851579..7b3df35 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ডিভাইসটি পুনরায় চালু হওয়ার পর প্যাটার্নের প্রয়োজন হবে"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ডিভাইসটি পুনরায় চালু হওয়ার পর পিন প্রয়োজন হবে"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ডিভাইসটি পুনরায় চালু হওয়ার পর পাসওয়ার্ডের প্রয়োজন হবে"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"অতিরিক্ত সুরক্ষার জন্য, এর বদলে প্যাটার্ন ব্যবহার করুন"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"অতিরিক্ত সুরক্ষার জন্য, এর বদলে পিন ব্যবহার করুন"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"অতিরিক্ত সুরক্ষার জন্য, এর বদলে পাসওয়ার্ড ব্যবহার করুন"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"প্রশাসক ডিভাইসটি লক করেছেন"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ডিভাইসটিকে ম্যানুয়ালি লক করা হয়েছে"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"শনাক্ত করা যায়নি"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ডিফল্ট"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"বাবল"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"অ্যানালগ"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"চালিয়ে যেতে আপনার ডিভাইস আনলক করুন"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index 4705b4d9..bb9e690 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Potreban je uzorak nakon što se uređaj ponovo pokrene"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Potreban je PIN nakon što se uređaj ponovo pokrene"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Potrebna je lozinka nakon što se uređaj ponovo pokrene"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Radi dodatne zaštite, umjesto toga koristite uzorak"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Radi dodatne zaštite, umjesto toga koristite PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Radi dodatne zašitite, umjesto toga koristite lozinku"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Uređaj je zaključao administrator"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznato"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Zadano"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Mjehurići"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogni"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Otključajte uređaj da nastavite"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 284eaeb..1c81c60 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Cal introduir el patró quan es reinicia el dispositiu"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Cal introduir el PIN quan es reinicia el dispositiu"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Cal introduir la contrasenya quan es reinicia el dispositiu"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Per a més seguretat, utilitza el patró"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Per a més seguretat, utilitza el PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Per a més seguretat, utilitza la contrasenya"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"L\'administrador ha bloquejat el dispositiu"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositiu s\'ha bloquejat manualment"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"No s\'ha reconegut"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Predeterminada"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bombolla"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analògica"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Desbloqueja el dispositiu per continuar"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index 6b4f607..9a6178c 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po restartování zařízení je vyžadováno gesto"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po restartování zařízení je vyžadován kód PIN"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po restartování zařízení je vyžadováno heslo"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Z bezpečnostních důvodů raději použijte gesto"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Z bezpečnostních důvodů raději použijte PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Z bezpečnostních důvodů raději použijte heslo"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Zařízení je uzamknuto administrátorem"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Zařízení bylo ručně uzamčeno"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nerozpoznáno"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Výchozí"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bublina"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogové"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Pokud chcete pokračovat, odemkněte zařízení"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index 85238df..aac1b83 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Du skal angive et mønster, når du har genstartet enheden"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Der skal angives en pinkode efter genstart af enheden"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Der skal angives en adgangskode efter genstart af enheden"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Øg sikkerheden ved at bruge dit oplåsningsmønter i stedet"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Øg sikkerheden ved at bruge din pinkode i stedet"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Øg sikkerheden ved at bruge din adgangskode i stedet"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Enheden er blevet låst af administratoren"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheden blev låst manuelt"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ikke genkendt"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Standard"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Boble"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Lås din enhed op for at fortsætte"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 18befed..5a340ff 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Nach dem Neustart des Geräts ist die Eingabe des Musters erforderlich"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Nach dem Neustart des Geräts ist die Eingabe der PIN erforderlich"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Nach dem Neustart des Geräts ist die Eingabe des Passworts erforderlich"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Verwende für mehr Sicherheit stattdessen dein Muster"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Verwende für mehr Sicherheit stattdessen deine PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Verwende für mehr Sicherheit stattdessen dein Passwort"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Gerät vom Administrator gesperrt"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Gerät manuell gesperrt"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nicht erkannt"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Standard"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Gerät entsperren, um fortzufahren"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 65b84486..973139f 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Απαιτείται μοτίβο μετά από την επανεκκίνηση της συσκευής"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Απαιτείται PIN μετά από την επανεκκίνηση της συσκευής"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Απαιτείται κωδικός πρόσβασης μετά από την επανεκκίνηση της συσκευής"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Για πρόσθετη ασφάλεια, χρησιμοποιήστε εναλλακτικά μοτίβο"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Για πρόσθετη ασφάλεια, χρησιμοποιήστε εναλλακτικά PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Για πρόσθετη ασφάλεια, χρησιμοποιήστε εναλλακτικά κωδικό πρόσβασης"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Η συσκευή κλειδώθηκε από τον διαχειριστή"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Η συσκευή κλειδώθηκε με μη αυτόματο τρόπο"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Δεν αναγνωρίστηκε"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Προεπιλογή"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Συννεφάκι"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Αναλογικό"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Ξεκλειδώστε τη συσκευή σας για να συνεχίσετε"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index 588f1b5..41eaa389 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"For additional security, use pattern instead"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"For additional security, use PIN instead"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"For additional security, use password instead"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Device locked by admin"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogue"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Unlock your device to continue"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 08fc8d6..c8ba237 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"For additional security, use pattern instead"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"For additional security, use PIN instead"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"For additional security, use password instead"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Device locked by admin"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogue"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Unlock your device to continue"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index 588f1b5..41eaa389 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"For additional security, use pattern instead"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"For additional security, use PIN instead"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"For additional security, use password instead"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Device locked by admin"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogue"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Unlock your device to continue"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index 588f1b5..41eaa389 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"For additional security, use pattern instead"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"For additional security, use PIN instead"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"For additional security, use password instead"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Device locked by admin"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogue"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Unlock your device to continue"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index c71a678..6314d90 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Se requiere el patrón después de reiniciar el dispositivo"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Se requiere el PIN después de reiniciar el dispositivo"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Se requiere la contraseña después de reiniciar el dispositivo"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Para seguridad adicional, usa un patrón"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Para seguridad adicional, usa un PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Para seguridad adicional, usa una contraseña"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Dispositivo bloqueado por el administrador"</string>
     <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>
@@ -93,6 +90,5 @@
     <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>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Desbloquea tu dispositivo para continuar"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index c6ee698..5aecf84 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Debes introducir el patrón después de reiniciar el dispositivo"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Debes introducir el PIN después de reiniciar el dispositivo"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Debes introducir la contraseña después de reiniciar el dispositivo"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Para mayor seguridad, usa el patrón"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Para mayor seguridad, usa el PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Para mayor seguridad, usa la contraseña"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Dispositivo bloqueado por el administrador"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositivo se ha bloqueado manualmente"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"No se reconoce"</string>
@@ -93,6 +90,5 @@
     <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>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Desbloquea tu dispositivo para continuar"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index 071ede8..9306ff6 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pärast seadme taaskäivitamist tuleb sisestada muster"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Pärast seadme taaskäivitamist tuleb sisestada PIN-kood"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Pärast seadme taaskäivitamist tuleb sisestada parool"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Kasutage tugevama turvalisuse huvides hoopis mustrit"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Kasutage tugevama turvalisuse huvides hoopis PIN-koodi"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Kasutage tugevama turvalisuse huvides hoopis parooli"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Administraator lukustas seadme"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Seade lukustati käsitsi"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ei tuvastatud"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Vaikenumbrilaud"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Mull"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analoog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Jätkamiseks avage oma seade"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 9b8e65b..4ebe0f0 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Eredua marraztu beharko duzu gailua berrabiarazten denean"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PINa idatzi beharko duzu gailua berrabiarazten denean"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Pasahitza idatzi beharko duzu gailua berrabiarazten denean"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Babestuago egoteko, erabili eredua"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Babestuago egoteko, erabili PINa"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Babestuago egoteko, erabili pasahitza"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Administratzaileak blokeatu egin du gailua"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Eskuz blokeatu da gailua"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ez da ezagutu"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Lehenetsia"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Puxikak"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogikoa"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Aurrera egiteko, desblokeatu gailua"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 3583f1e..e9a2e87 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"بعد از بازنشانی دستگاه باید الگو وارد شود"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"بعد از بازنشانی دستگاه باید پین وارد شود"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"بعد از بازنشانی دستگاه باید گذرواژه وارد شود"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"برای امنیت بیشتر، به‌جای آن از الگو استفاده کنید"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"برای امنیت بیشتر، به‌جای آن از پین استفاده کنید"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"برای امنیت بیشتر، به‌جای آن از گذرواژه استفاده کنید"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"دستگاه توسط سرپرست سیستم قفل شده است"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"دستگاه به‌صورت دستی قفل شده است"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"شناسایی نشد"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"پیش‌فرض"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"حباب"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"آنالوگ"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"برای ادامه، قفل دستگاهتان را باز کنید"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index a0ac6df..e80869a 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Kuvio vaaditaan laitteen uudelleenkäynnistyksen jälkeen."</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN-koodi vaaditaan laitteen uudelleenkäynnistyksen jälkeen."</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Salasana vaaditaan laitteen uudelleenkäynnistyksen jälkeen."</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Lisäsuojaa saat, kun käytät sen sijaan kuviota"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Lisäsuojaa saat, kun käytät sen sijaan PIN-koodia"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Lisäsuojaa saat, kun käytät sen sijaan salasanaa"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Järjestelmänvalvoja lukitsi laitteen."</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Laite lukittiin manuaalisesti"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ei tunnistettu"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Oletus"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Kupla"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analoginen"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Jatka avaamalla laitteen lukitus"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index ec00ba3..92d0617 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Veuillez dessiner le schéma après le redémarrage de l\'appareil"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Veuillez saisir le code après le redémarrage de l\'appareil"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Veuillez saisir le mot de passe après le redémarrage de l\'appareil"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Pour plus de sécurité, utilisez plutôt un schéma"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Pour plus de sécurité, utilisez plutôt un code"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Pour plus de sécurité, utilisez plutôt un mot de passe"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Appareil verrouillé par l\'administrateur"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Appareil verrouillé manuellement"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Non reconnu"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Par défaut"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bulle"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogique"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Déverrouillez votre appareil pour continuer"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index a3f8e86..776e90a 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"É necesario o padrón despois do reinicio do dispositivo"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"É necesario o PIN despois do reinicio do dispositivo"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"É necesario o contrasinal despois do reinicio do dispositivo"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Utiliza un padrón para obter maior seguranza"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Utiliza un PIN para obter maior seguranza"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Utiliza un contrasinal para obter maior seguranza"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"O administrador bloqueou o dispositivo"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo bloqueouse manualmente"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Non se recoñeceu"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Predeterminado"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Burbulla"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analóxico"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Desbloquea o dispositivo para continuar"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index c97fe01..a8b9a3a 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ઉપકરણનો પુનઃપ્રારંભ થાય તે પછી પૅટર્ન જરૂરી છે"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ઉપકરણનો પુનઃપ્રારંભ થાય તે પછી પિન જરૂરી છે"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ઉપકરણનો પુનઃપ્રારંભ થાય તે પછી પાસવર્ડ જરૂરી છે"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"વધારાની સુરક્ષા માટે, તેના બદલે પૅટર્નનો ઉપયોગ કરો"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"વધારાની સુરક્ષા માટે, તેના બદલે પિનનો ઉપયોગ કરો"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"વધારાની સુરક્ષા માટે, તેના બદલે પાસવર્ડનો ઉપયોગ કરો"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"વ્યવસ્થાપકે ઉપકરણ લૉક કરેલું છે"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ઉપકરણ મેન્યુઅલી લૉક કર્યું હતું"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ઓળખાયેલ નથી"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ડિફૉલ્ટ"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"બબલ"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"એનાલોગ"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"ચાલુ રાખવા માટે તમારા ડિવાઇસને અનલૉક કરો"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index 1283004..47560dd 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"डिवाइस फिर से चालू होने के बाद पैटर्न ज़रूरी है"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"डिवाइस फिर से चालू होने के बाद पिन ज़रूरी है"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"डिवाइस फिर से चालू होने के बाद पासवर्ड ज़रूरी है"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ज़्यादा सुरक्षा के लिए, इसके बजाय पैटर्न का इस्तेमाल करें"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ज़्यादा सुरक्षा के लिए, इसके बजाय पिन का इस्तेमाल करें"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ज़्यादा सुरक्षा के लिए, इसके बजाय पासवर्ड का इस्तेमाल करें"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"व्यवस्थापक ने डिवाइस को लॉक किया है"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"डिवाइस को मैन्युअल रूप से लॉक किया गया था"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"पहचान नहीं हो पाई"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"डिफ़ॉल्ट"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"बबल"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"एनालॉग"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"जारी रखने के लिए डिवाइस अनलॉक करें"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index 7a14a80..efd1cbb 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Nakon ponovnog pokretanja uređaja morate unijeti uzorak"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Nakon ponovnog pokretanja uređaja morate unijeti PIN"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Nakon ponovnog pokretanja uređaja morate unijeti zaporku"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Za dodatnu sigurnost upotrijebite uzorak"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Za dodatnu sigurnost upotrijebite PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Za dodatnu sigurnost upotrijebite zaporku"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Administrator je zaključao uređaj"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznato"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Zadano"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Mjehurić"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogni"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Otključajte uređaj da biste nastavili"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index a4fbf53..0421ff8 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Az eszköz újraindítását követően meg kell adni a mintát"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Az eszköz újraindítását követően meg kell adni a PIN-kódot"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Az eszköz újraindítását követően meg kell adni a jelszót"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"A nagyobb biztonság érdekében használjon inkább mintát"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"A nagyobb biztonság érdekében használjon inkább PIN-kódot"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"A nagyobb biztonság érdekében használjon inkább jelszót"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"A rendszergazda zárolta az eszközt"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Az eszközt manuálisan lezárták"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nem ismerhető fel"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Alapértelmezett"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Buborék"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analóg"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"A folytatáshoz oldja fel az eszközét"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index 086eeb9..d421c29 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել նախշը"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել PIN կոդը"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել գաղտնաբառը"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Լրացուցիչ անվտանգության համար օգտագործեք նախշ"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Լրացուցիչ անվտանգության համար օգտագործեք PIN կոդ"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Լրացուցիչ անվտանգության համար օգտագործեք գաղտնաբառ"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Սարքը կողպված է ադմինիստրատորի կողմից"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Սարքը կողպվել է ձեռքով"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Չհաջողվեց ճանաչել"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Կանխադրված"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Պղպջակ"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Անալոգային"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Շարունակելու համար ապակողպեք ձեր սարքը"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index b43a032..2061e85 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pola diperlukan setelah perangkat dimulai ulang"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN diperlukan setelah perangkat dimulai ulang"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Sandi diperlukan setelah perangkat dimulai ulang"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Untuk keamanan tambahan, gunakan pola"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Untuk keamanan tambahan, gunakan PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Untuk keamanan tambahan, gunakan sandi"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Perangkat dikunci oleh admin"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Perangkat dikunci secara manual"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Tidak dikenali"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Balon"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Buka kunci perangkat untuk melanjutkan"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index 8bad961..ae3da57 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Mynsturs er krafist þegar tækið er endurræst"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN-númers er krafist þegar tækið er endurræst"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Aðgangsorðs er krafist þegar tækið er endurræst"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Fyrir aukið öryggi skaltu nota mynstur í staðinn"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Fyrir aukið öryggi skaltu nota PIN-númer í staðinn"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Fyrir aukið öryggi skaltu nota aðgangsorð í staðinn"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Kerfisstjóri læsti tæki"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Tækinu var læst handvirkt"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Þekktist ekki"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Sjálfgefið"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Blaðra"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Með vísum"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Taktu tækið úr lás til að halda áfram"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 186177ff..d1feea6 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Sequenza obbligatoria dopo il riavvio del dispositivo"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN obbligatorio dopo il riavvio del dispositivo"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password obbligatoria dopo il riavvio del dispositivo"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Per maggior sicurezza, usa invece la sequenza"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Per maggior sicurezza, usa invece il PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Per maggior sicurezza, usa invece la password"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Dispositivo bloccato dall\'amministratore"</string>
     <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>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Predefinito"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bolla"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogico"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Sblocca il dispositivo per continuare"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index 123cc39..b56042a0 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"მოწყობილობის გადატვირთვის შემდეგ საჭიროა ნიმუშის დახატვა"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"მოწყობილობის გადატვირთვის შემდეგ საჭიროა PIN-კოდის შეყვანა"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"მოწყობილობის გადატვირთვის შემდეგ საჭიროა პაროლის შეყვანა"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"დამატებითი უსაფრთხოებისთვის გამოიყენეთ ნიმუში"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"დამატებითი უსაფრთხოებისთვის გამოიყენეთ PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"დამატებითი უსაფრთხოებისთვის გამოიყენეთ პაროლი"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"მოწყობილობა ჩაკეტილია ადმინისტრატორის მიერ"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"მოწყობილობა ხელით ჩაიკეტა"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"არ არის ამოცნობილი"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ნაგულისხმევი"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"ბუშტი"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"ანალოგური"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"გასაგრძელებლად განბლოკეთ თქვენი მოწყობილობა"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index 8daca5c..a4024de 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Құрылғы қайта іске қосылғаннан кейін, өрнекті енгізу қажет"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Құрылғы қайта іске қосылғаннан кейін, PIN кодын енгізу қажет"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Құрылғы қайта іске қосылғаннан кейін, құпия сөзді енгізу қажет"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Қосымша қауіпсіздік үшін өрнекті пайдаланыңыз."</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Қосымша қауіпсіздік үшін PIN кодын пайдаланыңыз."</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Қосымша қауіпсіздік үшін құпия сөзді пайдаланыңыз."</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Құрылғыны әкімші құлыптаған"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Құрылғы қолмен құлыпталды"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Танылмады"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Әдепкі"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Көпіршік"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Аналогтық"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Жалғастыру үшін құрылғының құлпын ашыңыз"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 73f507c..329912ab 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"តម្រូវឲ្យប្រើលំនាំ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"តម្រូវឲ្យបញ្ចូលកូដ PIN បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"តម្រូវឲ្យបញ្ចូលពាក្យសម្ងាត់ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ដើម្បីសុវត្ថិភាពបន្ថែម សូមប្រើលំនាំជំនួសវិញ"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ដើម្បីសុវត្ថិភាពបន្ថែម សូមប្រើកូដ PIN ជំនួសវិញ"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ដើម្បីសុវត្ថិភាពបន្ថែម សូមប្រើពាក្យសម្ងាត់ជំនួសវិញ"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"ឧបករណ៍​ត្រូវបាន​ចាក់សោ​ដោយអ្នក​គ្រប់គ្រង"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ឧបករណ៍ត្រូវបានចាក់សោដោយអ្នកប្រើផ្ទាល់"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"មិនអាចសម្គាល់បានទេ"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"លំនាំដើម"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"ពពុះ"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"អាណាឡូក"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"ដោះសោឧបករណ៍របស់អ្នកដើម្បីបន្ត"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index c279cea..d42d08d 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ಸಾಧನ ಮರುಪ್ರಾರಂಭಗೊಂಡ ನಂತರ ಪ್ಯಾಟರ್ನ್ ಅಗತ್ಯವಿರುತ್ತದೆ"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ಸಾಧನ ಮರುಪ್ರಾರಂಭಗೊಂಡ ನಂತರ ಪಿನ್ ಅಗತ್ಯವಿರುತ್ತದೆ"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ಸಾಧನ ಮರುಪ್ರಾರಂಭಗೊಂಡ ನಂತರ ಪಾಸ್‌ವರ್ಡ್ ಅಗತ್ಯವಿರುತ್ತದೆ"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ಹೆಚ್ಚುವರಿ ಭದ್ರತೆಗಾಗಿ, ಬದಲಿಗೆ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ಬಳಸಿ"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ಹೆಚ್ಚುವರಿ ಭದ್ರತೆಗಾಗಿ, ಬದಲಿಗೆ ಪಿನ್ ಬಳಸಿ"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ಹೆಚ್ಚುವರಿ ಭದ್ರತೆಗಾಗಿ, ಬದಲಿಗೆ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಬಳಸಿ"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"ನಿರ್ವಾಹಕರು ಸಾಧನವನ್ನು ಲಾಕ್ ಮಾಡಿದ್ದಾರೆ"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ಸಾಧನವನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ಲಾಕ್‌ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ಡೀಫಾಲ್ಟ್"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"ಬಬಲ್"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"ಅನಲಾಗ್"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"ಮುಂದುವರಿಸಲು, ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index 4c058ed..e916fee 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"기기가 다시 시작되면 패턴이 필요합니다."</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"기기가 다시 시작되면 PIN이 필요합니다."</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"기기가 다시 시작되면 비밀번호가 필요합니다."</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"보안 강화를 위해 대신 패턴 사용"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"보안 강화를 위해 대신 PIN 사용"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"보안 강화를 위해 대신 비밀번호 사용"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"관리자가 기기를 잠갔습니다."</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"기기가 수동으로 잠금 설정되었습니다."</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"인식할 수 없음"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"기본"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"버블"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"아날로그"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"기기를 잠금 해제하여 계속"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 7c7099e..88abd1e 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Түзмөк кайра күйгүзүлгөндөн кийин графикалык ачкычты тартуу талап кылынат"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Түзмөк кайра күйгүзүлгөндөн кийин PIN-кодду киргизүү талап кылынат"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Түзмөк кайра күйгүзүлгөндөн кийин сырсөздү киргизүү талап кылынат"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Кошумча коопсуздук үчүн анын ордуна графикалык ачкычты колдонуңуз"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Кошумча коопсуздук үчүн анын ордуна PIN кодду колдонуңуз"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Кошумча коопсуздук үчүн анын ордуна сырсөздү колдонуңуз"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Түзмөктү администратор кулпулап койгон"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Түзмөк кол менен кулпуланды"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Таанылган жок"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Демейки"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Көбүк"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Аналог"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Улантуу үчүн түзмөгүңүздүн кулпусун ачыңыз"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index 5a6df42..5001c30 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ຈຳເປັນຕ້ອງມີແບບຮູບປົດລັອກຫຼັງຈາກອຸປະກອນເລີ່ມລະບົບໃໝ່"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ຈຳເປັນຕ້ອງມີ PIN ຫຼັງຈາກອຸປະກອນເລີ່ມລະບົບໃໝ່"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ຈຳເປັນຕ້ອງມີລະຫັດຜ່ານຫຼັງຈາກອຸປະກອນເລີ່ມລະບົບໃໝ່"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ເພື່ອຄວາມປອດໄພເພີ່ມເຕີມ, ໃຫ້ໃຊ້ຮູບແບບແທນ"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ເພື່ອຄວາມປອດໄພເພີ່ມເຕີມ, ໃຫ້ໃຊ້ PIN ແທນ"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ເພື່ອຄວາມປອດໄພເພີ່ມເຕີມ, ໃຫ້ໃຊ້ລະຫັດຜ່ານແທນ"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"ອຸປະກອນຖືກລັອກໂດຍຜູ້ເບິ່ງແຍງລະບົບ"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ອຸປະກອນຖືກສັ່ງໃຫ້ລັອກ"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ບໍ່ຮູ້ຈັກ"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ຄ່າເລີ່ມຕົ້ນ"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"ຟອງ"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"ໂມງເຂັມ"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"ປົດລັອກອຸປະກອນຂອງທ່ານເພື່ອສືບຕໍ່"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 4d98fd1..20f6ad2 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Iš naujo paleidus įrenginį būtinas atrakinimo piešinys"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Iš naujo paleidus įrenginį būtinas PIN kodas"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Iš naujo paleidus įrenginį būtinas slaptažodis"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Papildomai saugai užtikrinti geriau naudokite atrakinimo piešinį"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Papildomai saugai užtikrinti geriau naudokite PIN kodą"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Papildomai saugai užtikrinti geriau naudokite slaptažodį"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Įrenginį užrakino administratorius"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Įrenginys užrakintas neautomatiškai"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Neatpažinta"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Numatytasis"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Debesėlis"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analoginis"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Įrenginio atrakinimas norint tęsti"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index 2660a06..7012c16 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pēc ierīces restartēšanas ir jāievada atbloķēšanas kombinācija."</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Pēc ierīces restartēšanas ir jāievada PIN kods."</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Pēc ierīces restartēšanas ir jāievada parole."</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Papildu drošībai izmantojiet kombināciju"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Papildu drošībai izmantojiet PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Papildu drošībai izmantojiet paroli"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Administrators bloķēja ierīci."</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Ierīce tika bloķēta manuāli."</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nav atpazīts"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Noklusējums"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Burbuļi"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogais"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Lai turpinātu, atbloķējiet ierīci"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index e62b435..7919773 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ഉപകരണം റീസ്റ്റാർട്ടായശേഷം ‌പാറ്റേൺ വരയ്‌ക്കേണ്ടതുണ്ട്"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ഉപകരണം റീസ്റ്റാർട്ടായശേഷം ‌പിൻ നൽകേണ്ടതുണ്ട്"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ഉപകരണം റീസ്റ്റാർട്ടായശേഷം ‌പാസ്‌വേഡ് നൽകേണ്ടതുണ്ട്"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"അധിക സുരക്ഷയ്ക്കായി, പകരം പാറ്റേൺ ഉപയോഗിക്കുക"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"അധിക സുരക്ഷയ്ക്കായി, പകരം പിൻ ഉപയോഗിക്കുക"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"അധിക സുരക്ഷയ്ക്കായി, പകരം പാസ്‍വേഡ് ഉപയോഗിക്കുക"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"ഉപകരണം അഡ്‌മിൻ ലോക്കുചെയ്തു"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ഉപകരണം നേരിട്ട് ലോക്കുചെയ്തു"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"തിരിച്ചറിയുന്നില്ല"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ഡിഫോൾട്ട്"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"ബബിൾ"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"അനലോഗ്"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"തുടരാൻ നിങ്ങളുടെ ഉപകരണം അൺലോക്ക് ചെയ്യുക"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index 1454b20..580b547a 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"डिव्हाइस रीस्टार्ट झाल्यावर पॅटर्न आवश्यक आहे"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"डिव्हाइस रीस्टार्ट झाल्यावर पिन आवश्यक आहे"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"डिव्हाइस रीस्टार्ट झाल्यावर पासवर्ड आवश्यक आहे"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"अतिरिक्त सुरक्षेसाठी, त्याऐवजी पॅटर्न वापरा"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"अतिरिक्त सुरक्षेसाठी, त्याऐवजी पिन वापरा"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"अतिरिक्त सुरक्षेसाठी, त्याऐवजी पासवर्ड वापरा"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"प्रशासकाद्वारे लॉक केलेले डिव्हाइस"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"डिव्हाइस मॅन्युअली लॉक केले होते"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ओळखले नाही"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"डीफॉल्ट"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"बबल"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"अ‍ॅनालॉग"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"पुढे सुरू ठेवण्यासाठी तुमचे डिव्हाइस अनलॉक करा"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index a6d1af9..c179dcb 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Corak diperlukan setelah peranti dimulakan semula"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN diperlukan setelah peranti dimulakan semula"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Kata laluan diperlukan setelah peranti dimulakan semula"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Untuk keselamatan tambahan, gunakan corak"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Untuk keselamatan tambahan, gunakan PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Untuk keselamatan tambahan, gunakan kata laluan"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Peranti dikunci oleh pentadbir"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Peranti telah dikunci secara manual"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Tidak dikenali"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Lalai"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Gelembung"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Buka kunci peranti anda untuk meneruskan"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 5617a11..7c69bdd 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"စက်ပစ္စည်းကို ပိတ်ပြီးပြန်ဖွင့်လိုက်သည့်အခါတွင် ပုံစံ လိုအပ်ပါသည်"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"စက်ပစ္စည်းကို ပိတ်ပြီးပြန်ဖွင့်လိုက်သည့်အခါတွင် ပင်နံပါတ် လိုအပ်ပါသည်"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"စက်ပစ္စည်းကို ပိတ်ပြီးပြန်ဖွင့်လိုက်သည့်အခါတွင် စကားဝှက် လိုအပ်ပါသည်"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ထပ်ဆောင်းလုံခြုံရေးအတွက် ၎င်းအစား ပုံစံသုံးပါ"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ထပ်ဆောင်းလုံခြုံရေးအတွက် ၎င်းအစား ပင်နံပါတ်သုံးပါ"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ထပ်ဆောင်းလုံခြုံရေးအတွက် ၎င်းအစား စကားဝှက်သုံးပါ"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"စက်ပစ္စည်းကို စီမံခန့်ခွဲသူက လော့ခ်ချထားပါသည်"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"စက်ပစ္စည်းကို ကိုယ်တိုင်ကိုယ်ကျ လော့ခ်ချထားခဲ့သည်"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"မသိ"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"မူလ"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"ပူဖောင်းကွက်"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"ရိုးရိုး"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"ရှေ့ဆက်ရန် သင့်စက်ကိုဖွင့်ပါ"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index 0ad9e95..e394d1f 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Du må tegne mønsteret etter at enheten har startet på nytt"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Du må skrive inn PIN-koden etter at enheten har startet på nytt"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Du må skrive inn passordet etter at enheten har startet på nytt"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Bruk mønster i stedet, for å øke sikkerheten"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Bruk PIN-kode i stedet, for å øke sikkerheten"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Bruk passord i stedet, for å øke sikkerheten"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Enheten er låst av administratoren"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheten ble låst manuelt"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ikke gjenkjent"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Standard"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Boble"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Lås opp enheten for å fortsette"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 196b74a..9f329e9 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"यन्त्र पुनः सुरु भएपछि ढाँचा आवश्यक पर्दछ"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"यन्त्र पुनः सुरु भएपछि PIN आवश्यक पर्दछ"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"यन्त्र पुनः सुरु भएपछि पासवर्ड आवश्यक पर्दछ"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"अतिरिक्त सुरक्षाका लागि यो प्रमाणीकरण विधिको साटो प्याटर्न प्रयोग गर्नुहोस्"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"अतिरिक्त सुरक्षाका लागि यो प्रमाणीकरण विधिको साटो पिन प्रयोग गर्नुहोस्"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"अतिरिक्त सुरक्षाका लागि यो प्रमाणीकरण विधिको साटो पासवर्ड प्रयोग गर्नुहोस्"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"प्रशासकले यन्त्रलाई लक गर्नुभएको छ"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"यन्त्रलाई म्यानुअल तरिकाले लक गरिएको थियो"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"पहिचान भएन"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"डिफल्ट"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"बबल"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"एनालग"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"आफ्नो डिभाइस अनलक गरी जारी राख्नुहोस्"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index 747b3bb..579824a 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Patroon vereist nadat het apparaat opnieuw is opgestart"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Pincode vereist nadat het apparaat opnieuw is opgestart"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Wachtwoord vereist nadat het apparaat opnieuw is opgestart"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Gebruik in plaats daarvan het patroon voor extra beveiliging"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Gebruik in plaats daarvan de pincode voor extra beveiliging"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Gebruik in plaats daarvan het wachtwoord voor extra beveiliging"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Apparaat vergrendeld door beheerder"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Apparaat is handmatig vergrendeld"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Niet herkend"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Standaard"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bel"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analoog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Ontgrendel je apparaat om door te gaan"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index bf1a359a..5c3fff7 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ਡੀਵਾਈਸ ਦੇ ਮੁੜ-ਚਾਲੂ ਹੋਣ \'ਤੇ ਪੈਟਰਨ ਦੀ ਲੋੜ ਹੈ"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ਡੀਵਾਈਸ ਦੇ ਮੁੜ-ਚਾਲੂ ਹੋਣ \'ਤੇ ਪਿੰਨ ਦੀ ਲੋੜ ਹੈ"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ਡੀਵਾਈਸ ਦੇ ਮੁੜ-ਚਾਲੂ ਹੋਣ \'ਤੇ ਪਾਸਵਰਡ ਦੀ ਲੋੜ ਹੈ"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ਵਧੀਕ ਸੁਰੱਖਿਆ ਲਈ, ਇਸਦੀ ਬਜਾਏ ਪੈਟਰਨ ਵਰਤੋ"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ਵਧੀਕ ਸੁਰੱਖਿਆ ਲਈ, ਇਸਦੀ ਬਜਾਏ ਪਿੰਨ ਵਰਤੋ"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ਵਧੀਕ ਸੁਰੱਖਿਆ ਲਈ, ਇਸਦੀ ਬਜਾਏ ਪਾਸਵਰਡ ਵਰਤੋ"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਡੀਵਾਈਸ ਨੂੰ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ਡੀਵਾਈਸ ਨੂੰ ਹੱਥੀਂ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"ਬੁਲਬੁਲਾ"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"ਐਨਾਲੌਗ"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਅਣਲਾਕ ਕਰੋ"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index c49149b..3736386 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po ponownym uruchomieniu urządzenia wymagany jest wzór"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po ponownym uruchomieniu urządzenia wymagany jest kod PIN"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po ponownym uruchomieniu urządzenia wymagane jest hasło"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Ze względów bezpieczeństwa użyj wzoru"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Ze względów bezpieczeństwa użyj kodu PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Ze względów bezpieczeństwa użyj hasła"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Urządzenie zablokowane przez administratora"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Urządzenie zostało zablokowane ręcznie"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nie rozpoznano"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Domyślna"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bąbelkowy"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogowy"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Odblokuj urządzenie, aby kontynuować"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index 547224e..67ae0fc 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Modelul este necesar după repornirea dispozitivului"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Codul PIN este necesar după repornirea dispozitivului"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Parola este necesară după repornirea dispozitivului"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Pentru mai multă securitate, folosește modelul"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Pentru mai multă securitate, folosește codul PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Pentru mai multă securitate, folosește parola"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Dispozitiv blocat de administrator"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Dispozitivul a fost blocat manual"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nu este recunoscut"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Prestabilit"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Balon"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogic"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Deblochează dispozitivul pentru a continua"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index e5862c3..82df4cb 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"උපාංගය නැවත ආරම්භ වූ පසු රටාව අවශ්‍යයි"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"උපාංගය නැවත ආරම්භ වූ පසු PIN අංකය අවශ්‍යයි"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"උපාංගය නැවත ආරම්භ වූ පසු මුරපදය අවශ්‍යයි"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"අතිරේක ආරක්ෂාව සඳහා, ඒ වෙනුවට රටාව භාවිතා කරන්න"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"අතිරේක ආරක්ෂාව සඳහා, ඒ වෙනුවට PIN භාවිතා කරන්න"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"අතිරේක ආරක්ෂාව සඳහා, ඒ වෙනුවට මුරපදය භාවිතා කරන්න"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"ඔබගේ පරිපාලක විසින් උපාංගය අගුළු දමා ඇත"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"උපාංගය හස්තීයව අගුලු දමන ලදී"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"හඳුනා නොගන්නා ලදී"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"පෙරනිමි"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"බුබුළ"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"ප්‍රතිසමය"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"ඉදිරියට යාමට ඔබේ උපාංගය අගුළු හරින්න"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index efe4ec8..2d8b3b1 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po reštartovaní zariadenia musíte zadať bezpečnostný vzor"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po reštartovaní zariadenia musíte zadať kód PIN"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po reštartovaní zariadenia musíte zadať heslo"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"V rámci zvýšenia zabezpečenia použite radšej vzor"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"V rámci zvýšenia zabezpečenia použite radšej PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"V rámci zvýšenia zabezpečenia použite radšej heslo"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Zariadenie zamkol správca"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Zariadenie bolo uzamknuté ručne"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nerozpoznané"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Predvolený"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bublina"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analógový"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Ak chcete pokračovať, odomknite zariadenie"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 52726c2..4c4ea06 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po vnovičnem zagonu naprave je treba vnesti vzorec"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po vnovičnem zagonu naprave je treba vnesti kodo PIN"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po vnovičnem zagonu naprave je treba vnesti geslo"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Za dodatno varnost raje uporabite vzorec."</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Za dodatno varnost raje uporabite kodo PIN."</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Za dodatno varnost raje uporabite geslo."</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Napravo je zaklenil skrbnik"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Naprava je bila ročno zaklenjena"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ni prepoznano"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Privzeto"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Mehurček"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogno"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Za nadaljevanje odklenite napravo"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index a0a5594..78e217d 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Kërkohet motivi pas rinisjes së pajisjes"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Kërkohet kodi PIN pas rinisjes së pajisjes"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Kërkohet fjalëkalimi pas rinisjes së pajisjes"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Për më shumë siguri, përdor motivin më mirë"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Për më shumë siguri, përdor kodin PIN më mirë"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Për më shumë siguri, përdor fjalëkalimin më mirë"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Pajisja është e kyçur nga administratori"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Pajisja është kyçur manualisht"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nuk njihet"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"E parazgjedhur"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Flluskë"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analoge"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Shkyç pajisjen tënde për të vazhduar"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index e634fdcb5..80d8755 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Треба да унесете шаблон када се уређај поново покрене"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Треба да унесете PIN када се уређај поново покрене"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Треба да унесете лозинку када се уређај поново покрене"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"За додатну безбедност користите шаблон"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"За додатну безбедност користите PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"За додатну безбедност користите лозинку"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Администратор је закључао уређај"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Уређај је ручно закључан"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Није препознат"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Подразумевани"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Мехурићи"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Аналогни"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Откључајте уређај да бисте наставили"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index fc9beb1..b5548b9 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Du måste rita mönster när du har startat om enheten"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Du måste ange pinkod när du har startat om enheten"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Du måste ange lösenord när du har startat om enheten"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"För ytterligare säkerhet använder du mönstret i stället"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"För ytterligare säkerhet använder du pinkoden i stället"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"För ytterligare säkerhet använder du lösenordet i stället"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Administratören har låst enheten"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheten har låsts manuellt"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Identifierades inte"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Standard"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bubbla"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Lås upp enheten för att fortsätta"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index bcab24b..02af18e 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Unafaa kuchora mchoro baada ya kuwasha kifaa upya"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Unafaa kuweka PIN baada ya kuwasha kifaa upya"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Unafaa kuweka nenosiri baada ya kuwasha kifaa upya"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Kwa usalama wa ziada, tumia mchoro badala yake"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Kwa usalama wa ziada, tumia PIN badala yake"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Kwa usalama wa ziada, tumia nenosiri badala yake"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Msimamizi amefunga kifaa"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Umefunga kifaa mwenyewe"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Haitambuliwi"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Chaguomsingi"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Kiputo"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analogi"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Fungua kifaa chako ili uendelee"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 88d5760..0d32d46 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"சாதனத்தை மீண்டும் தொடங்கியதும், பேட்டர்னை வரைய வேண்டும்"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"சாதனத்தை மீண்டும் தொடங்கியதும், பின்னை உள்ளிட வேண்டும்"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"சாதனத்தை மீண்டும் தொடங்கியதும், கடவுச்சொல்லை உள்ளிட வேண்டும்"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"கூடுதல் பாதுகாப்பிற்குப் பேட்டர்னைப் பயன்படுத்தவும்"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"கூடுதல் பாதுகாப்பிற்குப் பின்னை (PIN) பயன்படுத்தவும்"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"கூடுதல் பாதுகாப்பிற்குக் கடவுச்சொல்லைப் பயன்படுத்தவும்"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"நிர்வாகி சாதனத்தைப் பூட்டியுள்ளார்"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"பயனர் சாதனத்தைப் பூட்டியுள்ளார்"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"அடையாளங்காணபடவில்லை"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"இயல்பு"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"பபிள்"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"அனலாக்"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"தொடர, சாதனத்தை அன்லாக் செய்யுங்கள்"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 3a0111a..f519daf 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"పరికరాన్ని పునఃప్రారంభించిన తర్వాత నమూనాను గీయాలి"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"డివైజ్‌ను పునఃప్రారంభించిన తర్వాత పిన్ నమోదు చేయాలి"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"పరికరాన్ని పునఃప్రారంభించిన తర్వాత పాస్‌వర్డ్‌ను నమోదు చేయాలి"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"అదనపు సెక్యూరిటీ కోసం, బదులుగా ఆకృతిని ఉపయోగించండి"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"అదనపు సెక్యూరిటీ కోసం, బదులుగా PINను ఉపయోగించండి"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"అదనపు సెక్యూరిటీ కోసం, బదులుగా పాస్‌వర్డ్‌ను ఉపయోగించండి"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"పరికరం నిర్వాహకుల ద్వారా లాక్ చేయబడింది"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"పరికరం మాన్యువల్‌గా లాక్ చేయబడింది"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"గుర్తించలేదు"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ఆటోమేటిక్"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"బబుల్"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"ఎనలాగ్"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"కొనసాగించడానికి మీ పరికరాన్ని అన్‌లాక్ చేయండి"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index e520762..80dae8c 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Cihaz yeniden başladıktan sonra desen gerekir"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Cihaz yeniden başladıktan sonra PIN gerekir"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Cihaz yeniden başladıktan sonra şifre gerekir"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Ek güvenlik için bunun yerine desen kullanın"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Ek güvenlik için bunun yerine PIN kullanın"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Ek güvenlik için bunun yerine şifre kullanın"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Cihaz, yönetici tarafından kilitlendi"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Cihazın manuel olarak kilitlendi"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Tanınmadı"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Varsayılan"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Baloncuk"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Devam etmek için cihazınızın kilidini açın"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index 613181d..ff594ae 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Після перезавантаження пристрою потрібно ввести ключ"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Після перезавантаження пристрою потрібно ввести PIN-код"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Після перезавантаження пристрою потрібно ввести пароль"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"З міркувань додаткової безпеки скористайтеся ключем"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"З міркувань додаткової безпеки скористайтеся PIN-кодом"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"З міркувань додаткової безпеки скористайтеся паролем"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Адміністратор заблокував пристрій"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Пристрій заблоковано вручну"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Не розпізнано"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"За умовчанням"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Бульбашковий"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Аналоговий"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Розблокуйте пристрій, щоб продовжити"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index a122f85..9308260 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"آلہ دوبارہ چالو ہونے کے بعد پیٹرن درکار ہوتا ہے"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"‏آلہ دوبارہ چالو ہونے کے بعد PIN درکار ہوتا ہے"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"آلہ دوبارہ چالو ہونے کے بعد پاس ورڈ درکار ہوتا ہے"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"اضافی سیکیورٹی کے لئے، اس کے بجائے پیٹرن استعمال کریں"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"‏اضافی سیکیورٹی کے لئے، اس کے بجائے PIN استعمال کریں"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"اضافی سیکیورٹی کے لئے، اس کے بجائے پاس ورڈ استعمال کریں"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"آلہ منتظم کی جانب سے مقفل ہے"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"آلہ کو دستی طور پر مقفل کیا گیا تھا"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"تسلیم شدہ نہیں ہے"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"ڈیفالٹ"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"بلبلہ"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"اینالاگ"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"جاری رکھنے کے لئے اپنا آلہ غیر مقفل کریں"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index e7c9295..2771ada 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Yêu cầu hình mở khóa sau khi thiết bị khởi động lại"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Yêu cầu mã PIN sau khi thiết bị khởi động lại"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Yêu cầu mật khẩu sau khi thiết bị khởi động lại"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Để tăng cường bảo mật, hãy sử dụng hình mở khoá"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Để tăng cường bảo mật, hãy sử dụng mã PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Để tăng cường bảo mật, hãy sử dụng mật khẩu"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Thiết bị đã bị quản trị viên khóa"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Thiết bị đã bị khóa theo cách thủ công"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Không nhận dạng được"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Mặc định"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bong bóng"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Đồng hồ kim"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Mở khoá thiết bị của bạn để tiếp tục"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index d37d645..fb92838 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"重启设备后需要绘制解锁图案"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"重启设备后需要输入 PIN 码"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"重启设备后需要输入密码"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"为增强安全性,请改用图案"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"为增强安全性,请改用 PIN 码"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"为增强安全性,请改用密码"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"管理员已锁定设备"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"此设备已手动锁定"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"无法识别"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"默认"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"泡泡"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"指针"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"解锁设备才能继续操作"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index 9dbb8f2..49050e5 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"裝置重新啟動後,必須畫出上鎖圖案才能使用"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"裝置重新啟動後,必須輸入 PIN 碼才能使用"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"裝置重新啟動後,必須輸入密碼才能使用"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"為提升安全性,請改用圖案"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"為提升安全性,請改用 PIN"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"為提升安全性,請改用密碼"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"裝置已由管理員鎖定"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"使用者已手動將裝置上鎖"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"未能識別"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"預設"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"泡泡"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"指針"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"解鎖裝置以繼續"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index ebb88e1..e5a363c 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"裝置重新啟動後需要畫出解鎖圖案"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"裝置重新啟動後需要輸入 PIN 碼"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"裝置重新啟動後需要輸入密碼"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"為強化安全性,請改用解鎖圖案"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"為強化安全性,請改用 PIN 碼"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"為強化安全性,請改用密碼"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"管理員已鎖定裝置"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"裝置已手動鎖定"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"無法識別"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"預設"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"泡泡"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"類比"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"解鎖裝置才能繼續操作"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 57e56f7..72ca6c0 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -78,12 +78,9 @@
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Iphethini iyadingeka ngemuva kokuqala kabusha kwedivayisi"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Iphinikhodi iyadingeka ngemuva kokuqala kabusha kwedivayisi"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Iphasiwedi iyadingeka ngemuva kokuqala kabusha kwedivayisi"</string>
-    <!-- no translation found for kg_prompt_reason_timeout_pattern (5514969660010197363) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_pin (4227962059353859376) -->
-    <skip />
-    <!-- no translation found for kg_prompt_reason_timeout_password (8810879144143933690) -->
-    <skip />
+    <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"Ukuze uthole ukuvikeleka okwengeziwe, sebenzisa iphetheni esikhundleni salokho"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"Ukuze uthole ukuvikeleka okwengeziwe, sebenzisa i-PIN esikhundleni salokho"</string>
+    <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"Ukuze uthole ukuvikeleka okwengeziwe, sebenzisa iphasiwedi esikhundleni salokho"</string>
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Idivayisi ikhiywe ngumlawuli"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Idivayisi ikhiywe ngokwenza"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Akwaziwa"</string>
@@ -93,6 +90,5 @@
     <string name="clock_title_default" msgid="6342735240617459864">"Okuzenzekelayo"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Ibhamuza"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"I-Analog"</string>
-    <!-- no translation found for keyguard_unlock_to_continue (7509503484250597743) -->
-    <skip />
+    <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Vula idivayisi yakho ukuze uqhubeke"</string>
 </resources>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
index 3bcc37a..e2ce34f 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
@@ -14,7 +14,7 @@
   ~ limitations under the License.
   -->
 
-<com.android.systemui.biometrics.AuthCredentialPasswordView
+<com.android.systemui.biometrics.ui.CredentialPasswordView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -86,4 +86,4 @@
 
     </LinearLayout>
 
-</com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file
+</com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
index a3dd334..6e0e38b 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
@@ -14,7 +14,7 @@
   ~ limitations under the License.
   -->
 
-<com.android.systemui.biometrics.AuthCredentialPatternView
+<com.android.systemui.biometrics.ui.CredentialPatternView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -83,4 +83,4 @@
 
     </FrameLayout>
 
-</com.android.systemui.biometrics.AuthCredentialPatternView>
\ No newline at end of file
+</com.android.systemui.biometrics.ui.CredentialPatternView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 774b335f..021ebe6 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -14,7 +14,7 @@
   ~ limitations under the License.
   -->
 
-<com.android.systemui.biometrics.AuthCredentialPasswordView
+<com.android.systemui.biometrics.ui.CredentialPasswordView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -83,4 +83,4 @@
 
     </LinearLayout>
 
-</com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file
+</com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index 4af9970..891c6af 100644
--- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -14,7 +14,7 @@
   ~ limitations under the License.
   -->
 
-<com.android.systemui.biometrics.AuthCredentialPatternView
+<com.android.systemui.biometrics.ui.CredentialPatternView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -78,4 +78,4 @@
             android:layout_gravity="center_horizontal|bottom"/>
     </FrameLayout>
 
-</com.android.systemui.biometrics.AuthCredentialPatternView>
\ No newline at end of file
+</com.android.systemui.biometrics.ui.CredentialPatternView>
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index c526d9c..9b8b611 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -44,6 +44,15 @@
         android:background="@drawable/qs_media_outline_album_bg"
         />
 
+    <com.android.systemui.ripple.MultiRippleView
+        android:id="@+id/touch_ripple_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_media_session_height_expanded"
+        app:layout_constraintStart_toStartOf="@id/album_art"
+        app:layout_constraintEnd_toEndOf="@id/album_art"
+        app:layout_constraintTop_toTopOf="@id/album_art"
+        app:layout_constraintBottom_toBottomOf="@id/album_art" />
+
     <!-- Guideline for output switcher -->
     <androidx.constraintlayout.widget.Guideline
         android:id="@+id/center_vertical_guideline"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 92ef3f8..159323a 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -42,12 +42,6 @@
         android:clipToPadding="false"
         android:clipChildren="false">
 
-        <ViewStub
-            android:id="@+id/qs_header_stub"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-        />
-
         <include
             layout="@layout/keyguard_status_view"
             android:visibility="gone"/>
@@ -69,6 +63,15 @@
             systemui:layout_constraintBottom_toBottomOf="parent"
         />
 
+        <!-- This view should be after qs_frame so touches are dispatched first to it. That gives
+             it a chance to capture clicks before the NonInterceptingScrollView disallows all
+             intercepts -->
+        <ViewStub
+            android:id="@+id/qs_header_stub"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+        />
+
         <androidx.constraintlayout.widget.Guideline
             android:id="@+id/qs_edge_guideline"
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
index c2c79cb..78884ff 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
@@ -14,58 +14,84 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.systemui.user.UserSwitcherRootView
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/user_switcher_root"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_marginVertical="40dp"
-    android:layout_marginHorizontal="60dp">
+    android:orientation="vertical">
 
-  <androidx.constraintlayout.helper.widget.Flow
-      android:id="@+id/flow"
-      android:layout_width="0dp"
-      android:layout_height="wrap_content"
-      app:layout_constraintBottom_toBottomOf="parent"
-      app:layout_constraintTop_toTopOf="parent"
-      app:layout_constraintStart_toStartOf="parent"
-      app:layout_constraintEnd_toEndOf="parent"
-      app:flow_horizontalBias="0.5"
-      app:flow_verticalAlign="center"
-      app:flow_wrapMode="chain"
-      app:flow_horizontalGap="@dimen/user_switcher_fullscreen_horizontal_gap"
-      app:flow_verticalGap="44dp"
-      app:flow_horizontalStyle="packed"/>
+  <ScrollView
+      android:layout_width="match_parent"
+      android:layout_height="0dp"
+      android:layout_weight="1"
+      android:fillViewport="true">
 
-  <TextView
-      android:id="@+id/cancel"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:layout_gravity="center"
-      android:gravity="center"
-      app:layout_constraintHeight_min="48dp"
-      app:layout_constraintEnd_toStartOf="@+id/add"
-      app:layout_constraintBottom_toBottomOf="parent"
-      android:paddingHorizontal="@dimen/user_switcher_fullscreen_button_padding"
-      android:textSize="@dimen/user_switcher_fullscreen_button_text_size"
-      android:textColor="?androidprv:attr/colorAccentPrimary"
-      android:text="@string/cancel" />
+    <com.android.systemui.user.UserSwitcherRootView
+        android:id="@+id/user_switcher_grid_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="40dp"
+        android:paddingHorizontal="60dp">
 
-  <TextView
-      android:id="@+id/add"
-      style="@style/Widget.Dialog.Button.BorderButton"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:layout_gravity="center"
-      android:gravity="center"
-      android:paddingHorizontal="@dimen/user_switcher_fullscreen_button_padding"
-      android:text="@string/add"
-      android:textColor="?androidprv:attr/colorAccentPrimary"
-      android:textSize="@dimen/user_switcher_fullscreen_button_text_size"
-      android:visibility="gone"
-      app:layout_constraintBottom_toBottomOf="parent"
-      app:layout_constraintEnd_toEndOf="parent"
-      app:layout_constraintHeight_min="48dp" />
-</com.android.systemui.user.UserSwitcherRootView>
+      <androidx.constraintlayout.helper.widget.Flow
+          android:id="@+id/flow"
+          android:layout_width="0dp"
+          android:layout_height="wrap_content"
+          app:layout_constraintBottom_toBottomOf="parent"
+          app:layout_constraintTop_toTopOf="parent"
+          app:layout_constraintStart_toStartOf="parent"
+          app:layout_constraintEnd_toEndOf="parent"
+          app:flow_horizontalBias="0.5"
+          app:flow_verticalAlign="center"
+          app:flow_wrapMode="chain"
+          app:flow_horizontalGap="@dimen/user_switcher_fullscreen_horizontal_gap"
+          app:flow_verticalGap="44dp"
+          app:flow_horizontalStyle="packed"/>
+    </com.android.systemui.user.UserSwitcherRootView>
+
+  </ScrollView>
+
+  <LinearLayout
+    android:layout_width="match_parent"
+    android:layout_height="96dp"
+    android:orientation="horizontal"
+    android:gravity="center_vertical|end"
+    android:paddingEnd="48dp">
+
+    <TextView
+        android:id="@+id/cancel"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:gravity="center"
+        android:minHeight="48dp"
+        android:paddingHorizontal="@dimen/user_switcher_fullscreen_button_padding"
+        android:textSize="@dimen/user_switcher_fullscreen_button_text_size"
+        android:textColor="?androidprv:attr/colorAccentPrimary"
+        android:text="@string/cancel" />
+
+    <Space
+        android:layout_width="24dp"
+        android:layout_height="0dp"
+        />
+
+    <TextView
+        android:id="@+id/add"
+        style="@style/Widget.Dialog.Button.BorderButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:gravity="center"
+        android:paddingHorizontal="@dimen/user_switcher_fullscreen_button_padding"
+        android:text="@string/add"
+        android:textColor="?androidprv:attr/colorAccentPrimary"
+        android:textSize="@dimen/user_switcher_fullscreen_button_text_size"
+        android:visibility="gone"
+        android:minHeight="48dp" />
+
+  </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 3dc85d70..2f2780b 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleuromkering"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Kleurregstelling"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Gebruikerinstellings"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Maak toe"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Gekoppel"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Skakel mobiele data af?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Jy sal nie deur <xliff:g id="CARRIER">%s</xliff:g> toegang tot data of die internet hê nie. Internet sal net deur Wi-Fi beskikbaar wees."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"jou diensverskaffer"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Instellings kan nie jou antwoord verifieer nie omdat \'n program \'n toestemmingversoek verberg."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Laat <xliff:g id="APP_0">%1$s</xliff:g> toe om <xliff:g id="APP_2">%2$s</xliff:g>-skyfies te wys?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Dit kan inligting van <xliff:g id="APP">%1$s</xliff:g> af lees"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Vergrootglasvensterinstellings"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tik om toeganklikheidkenmerke oop te maak Pasmaak of vervang knoppie in Instellings.\n\n"<annotation id="link">"Bekyk instellings"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Skuif knoppie na kant om dit tydelik te versteek"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Beweeg na links bo"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Beweeg na regs bo"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Beweeg na links onder"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Beweeg na regs onder"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Beweeg na rand en versteek"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Beweeg weg van rand en wys"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"wissel"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Toestelkontroles"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Kies program om kontroles by te voeg"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobiele data sal nie outomaties koppel nie"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Geen verbinding nie"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Geen ander netwerke beskikbaar nie"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index fb60f9b..3ccb686 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ብሩህነት"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ተቃራኒ ቀለም"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"የቀለም ማስተካከያ"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"የተጠቃሚ ቅንብሮች"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"ተከናውኗል"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ዝጋ"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"ተገናኝቷል"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"የተንቀሳቃሽ ስልክ ውሂብ ይጥፋ?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"በ<xliff:g id="CARRIER">%s</xliff:g> በኩል የውሂብ ወይም የበይነመረቡ መዳረሻ አይኖረዎትም። በይነመረብ በWi-Fi በኩል ብቻ ነው የሚገኝ የሚሆነው።"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"የእርስዎ አገልግሎት አቅራቢ"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"አንድ መተግበሪያ የፍቃድ ጥያቄ እያገደ ስለሆነ ቅንብሮች ጥያቄዎን ማረጋገጥ አይችሉም።"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> የ<xliff:g id="APP_2">%2$s</xliff:g> ቁራጮችን እንዲያሳይ ይፈቀድለት?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ከ<xliff:g id="APP">%1$s</xliff:g> የመጣ መረጃን ማንበብ ይችላል"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"የማጉያ መስኮት ቅንብሮች"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"የተደራሽነት ባህሪያትን ለመክፈት መታ ያድርጉ። ይህንን አዝራር በቅንብሮች ውስጥ ያብጁ ወይም ይተኩ።\n\n"<annotation id="link">"ቅንብሮችን አሳይ"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ለጊዜው ለመደበቅ አዝራሩን ወደ ጠርዝ ያንቀሳቅሱ"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ወደ ላይኛው ግራ አንቀሳቅስ"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"የግርጌውን ግራ አንቀሳቅስ"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ታችኛውን ቀኝ አንቀሳቅስ"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ወደ ጠርዝ አንቀሳቅስ እና ደደብቅ"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ጠርዙን ወደ ውጭ አንቀሳቅስ እና አሳይ"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ቀያይር"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"የመሣሪያ መቆጣጠሪያዎች"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"መቆጣጠሪያዎችን ለማከል መተግበሪያ ይምረጡ"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"የተንቀሳቃሽ ስልክ ውሂብ በራስ-ሰር አይገናኝም"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"ግንኙነት የለም"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ሌላ አውታረ መረብ የሉም"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index f8567c7..c90fb25 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"السطوع"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"قلب الألوان"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"تصحيح الألوان"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"إعدادات المستخدم"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"تم"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"إغلاق"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"متصل"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"هل تريد إيقاف بيانات الجوّال؟"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"‏لن تتمكّن من استخدام البيانات أو الإنترنت من خلال <xliff:g id="CARRIER">%s</xliff:g>. ولن يتوفر اتصال الإنترنت إلا عبر Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"مشغّل شبكة الجوّال"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"لا يمكن للإعدادات التحقق من ردك لأن هناك تطبيقًا يحجب طلب الإذن."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"هل تريد السماح لتطبيق <xliff:g id="APP_0">%1$s</xliff:g> بعرض شرائح <xliff:g id="APP_2">%2$s</xliff:g>؟"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- يستطيع قراءة المعلومات من <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"إعدادات نافذة مكبّر الشاشة"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"انقر لفتح ميزات تسهيل الاستخدام. يمكنك تخصيص هذا الزر أو استبداله من الإعدادات.\n\n"<annotation id="link">"عرض الإعدادات"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"يمكنك نقل الزر إلى الحافة لإخفائه مؤقتًا."</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"نقل إلى أعلى يمين الشاشة"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"نقل إلى أعلى يسار الشاشة"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"نقل إلى أسفل يمين الشاشة"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"نقل إلى أسفل يسار الشاشة"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"نقله إلى الحافة وإخفاؤه"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"نقله إلى خارج الحافة وإظهاره"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"إيقاف/تفعيل"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"التحكم بالجهاز"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"اختيار تطبيق لإضافة عناصر التحكّم"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"لن يتم تلقائيًا الاتصال ببيانات الجوّال."</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"لا يتوفّر اتصال بالإنترنت"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"لا تتوفّر شبكات أخرى."</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 4fca5f2e..c363eee 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ৰং বিপৰীতকৰণ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ৰং শুধৰণী"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ব্যৱহাৰকাৰীৰ ছেটিং"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন কৰা হ’ল"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"বন্ধ কৰক"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"সংযোগ কৰা হ’ল"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"ম’বাইল ডেটা অফ কৰিবনে?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"আপুনি <xliff:g id="CARRIER">%s</xliff:g>ৰ জৰিয়তে ডেটা সংযোগ বা ইণ্টাৰনেট সংযোগ নাপাব। কেৱল ৱাই-ফাইৰ যোগেৰে ইণ্টাৰনেট উপলব্ধ হ\'ব।"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"আপোনাৰ বাহক"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"এটা এপে অনুমতি বিচাৰি কৰা অনুৰোধ এটা ঢাকি ধৰা বাবে ছেটিঙৰ পৰা আপোনাৰ উত্তৰ সত্যাপন কৰিব পৰা নাই।"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g>ক <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাবলৈ অনুমতি দিবনে?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ তথ্য পঢ়িব পাৰে"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"বিবৰ্ধকৰ ৱিণ্ড’ৰ ছেটিং"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"সাধ্য সুবিধাসমূহ খুলিবলৈ টিপক। ছেটিঙত এই বুটামটো কাষ্টমাইজ অথবা সলনি কৰক।\n\n"<annotation id="link">"ছেটিং চাওক"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"বুটামটোক সাময়িকভাৱে লুকুৱাবলৈ ইয়াক একেবাৰে কাষলৈ লৈ যাওক"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"শীৰ্ষৰ বাওঁফালে নিয়ক"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"তলৰ বাওঁফালে নিয়ক"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"তলৰ সোঁফালে নিয়ক"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"কাষলৈ নিয়ক আৰু লুকুৱাওক"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"কাষৰ বাহিৰলৈ নিয়ক আৰু দেখুৱাওক"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ট’গল কৰক"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইচৰ নিয়ন্ত্ৰণসমূহ"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"নিয়ন্ত্ৰণসমূহ যোগ কৰিবলৈ এপ্‌ বাছনি কৰক"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"ম’বাইল ডেটা স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"সংযোগ নাই"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনো নেটৱৰ্ক উপলব্ধ নহয়"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 7fa603f..c5f143b 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaqlıq"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rəng inversiyası"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Rəng korreksiyası"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"İstifadəçi ayarları"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Hazır"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Bağlayın"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Qoşulu"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Mobil data söndürülsün?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> ilə data və ya internetə daxil ola bilməyəcəksiniz. İnternet yalnız Wi-Fi ilə əlçatan olacaq."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatorunuz"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Tətbiq icazə sorğusunu gizlətdiyi üçün Ayarlar cavabınızı doğrulaya bilməz."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> tətbiqinə <xliff:g id="APP_2">%2$s</xliff:g> hissələrini göstərmək üçün icazə verilsin?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g> tətbiqindən məlumat oxuya bilər"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Böyüdücü pəncərə ayarları"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Əlçatımlılıq funksiyalarını açmaq üçün toxunun. Ayarlarda bu düyməni fərdiləşdirin və ya dəyişdirin.\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düyməni müvəqqəti gizlətmək üçün kənara çəkin"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuxarıya sola köçürün"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Yuxarıya sağa köçürün"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Aşağıya sola köçürün"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Aşağıya sağa köçürün"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"İçəri keçirib gizlədin"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Kənara daşıyıb göstərin"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"keçirin"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Cihaz kontrolları"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Kontrol əlavə etmək üçün tətbiq seçin"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobil data avtomatik qoşulmayacaq"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bağlantı yoxdur"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Heç bir başqa şəbəkə əlçatan deyil"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index ffe49b2..e61b692 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvetljenost"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcija boja"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisnička podešavanja"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zatvori"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Povezan"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Želite da isključite mobilne podatke?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup podacima ili internetu preko mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo preko WiFi veze."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"mobilni operater"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Podešavanja ne mogu da verifikuju vaš odgovor jer aplikacija skriva zahtev za dozvolu."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Želite li da dozvolite aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isečke iz aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Može da čita podatke iz aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Podešavanja prozora za uvećanje"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite za funkcije pristupačnosti. Prilagodite ili zamenite ovo dugme u Podešavanjima.\n\n"<annotation id="link">"Podešavanja"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pomerite dugme do ivice da biste ga privremeno sakrili"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premesti gore levo"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premesti gore desno"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Premesti dole levo"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Premesti dole desno"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Premesti do ivice i sakrij"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Premesti izvan ivice i prikaži"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"uključite/isključite"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Odaberite aplikaciju za dodavanje kontrola"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Nije uspelo autom. povezivanje preko mob. podataka"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Veza nije uspostavljena"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 3e76985..827370f 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркасць"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Інверсія колераў"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Карэкцыя колераў"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Налады карыстальніка"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Гатова"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Закрыць"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Падлучана"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Выключыць мабільную перадачу даных?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"У вас не будзе доступу да даных ці інтэрнэту праз аператара <xliff:g id="CARRIER">%s</xliff:g>. Інтэрнэт будзе даступны толькі праз Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ваш аператар"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Праграма хавае запыт на дазвол, таму ваш адказ немагчыма спраўдзіць у Наладах."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Дазволіць праграме <xliff:g id="APP_0">%1$s</xliff:g> паказваць зрэзы праграмы <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Можа счытваць інфармацыю з праграмы <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Налады акна лупы"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Націсніце, каб адкрыць спецыяльныя магчымасці. Рэгулюйце ці замяняйце кнопку ў Наладах.\n\n"<annotation id="link">"Прагляд налад"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Каб часова схаваць кнопку, перамясціце яе на край"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перамясціць лявей і вышэй"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перамясціць правей і вышэй"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Перамясціць лявей і ніжэй"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Перамясціць правей і ніжэй"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Перамясціць на край і схаваць"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Перамясціць за край і паказаць"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"уключыць/выключыць"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Элементы кіравання прыладай"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Выберыце праграму для дадавання элементаў кіравання"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Мабільная перадача даных не ўключаецца аўтаматычна"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Няма падключэння"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Больш няма даступных сетак"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 572e13f..880445b 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркост"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Цветове: инверт."</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекция на цветове"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Потребителски настройки"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Затваряне"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Установена е връзка"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Да се изключат ли мобилните данни?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Няма да можете да използвате данни или интернет чрез <xliff:g id="CARRIER">%s</xliff:g>. Ще имате достъп до интернет само през Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"оператора си"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"От Настройки не може да се получи потвърждение за отговора ви, защото заявката за разрешение се прикрива от приложение."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Искате ли да разрешите на <xliff:g id="APP_0">%1$s</xliff:g> да показва части от <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Може да чете информация от <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Настройки за инструмента за увеличаване на прозорци"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Докоснете, за да отворите функциите за достъпност. Персон./заменете бутона от настройките.\n\n"<annotation id="link">"Преглед на настройките"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Преместете бутона до края, за да го скриете временно"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Преместване горе вляво"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Преместване горе вдясно"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Преместване долу вляво"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Преместване долу вдясно"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Преместване в края и скриване"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Преместване в края и показване"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"превключване"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Контроли за устройството"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Изберете приложение, за да добавите контроли"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Връзката за мобилни данни няма да е автоматична"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Няма връзка"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Няма други налични мрежи"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 645f6ab..c74fc69 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"কালার ইনভার্সন"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"রঙ সংশোধন"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ব্যবহারকারী সেটিংস"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন হয়েছে"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"বন্ধ করুন"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"সংযুক্ত হয়েছে"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"মোবাইল ডেটা বন্ধ করবেন?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"আপনি \'<xliff:g id="CARRIER">%s</xliff:g>\'-এর মাধ্যমে ডেটা অথবা ইন্টারনেট অ্যাক্সেস করতে পারবেন না। শুধুমাত্র ওয়াই-ফাইয়ের মাধ্যমেই ইন্টারনেট অ্যাক্সেস করা যাবে।"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"আপনার পরিষেবা প্রদানকারী"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"একটি অ্যাপ কোনও অনুমোদনের অনুরোধকে ঢেকে দিচ্ছে, তাই সেটিংস থেকে আপনার প্রতিক্রিয়া যাচাই করা যাচ্ছে না।"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> অ্যাপটিকে <xliff:g id="APP_2">%2$s</xliff:g> এর অংশ দেখানোর অনুমতি দেবেন?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- এটি <xliff:g id="APP">%1$s</xliff:g> এর তথ্য অ্যাক্সেস করতে পারবে"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"\'ম্যাগনিফায়ার উইন্ডো\' সেটিংস"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"অ্যাক্সেসিবিলিটি ফিচার খুলতে ট্যাপ করুন। কাস্টমাইজ করুন বা সেটিংসে এই বোতামটি সরিয়ে দিন।\n\n"<annotation id="link">"সেটিংস দেখুন"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"এটি অস্থায়ীভাবে লুকাতে বোতামটি কোণে সরান"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"উপরে বাঁদিকে সরান"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"উপরে ডানদিকে সরান"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"নিচে বাঁদিকে সরান"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"নিচে ডান দিকে সরান"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"প্রান্তে যান ও আড়াল করুন"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"প্রান্ত থেকে সরান এবং দেখুন"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"টগল করুন"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইস কন্ট্রোল"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"কন্ট্রোল যোগ করতে অ্যাপ বেছে নিন"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"মোবাইল ডেটা নিজে থেকে কানেক্ট হবে না"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"কানেকশন নেই"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনও নেটওয়ার্ক উপলভ্য নেই"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 183dfe0..5356d74 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvjetljenje"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ispravka boja"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisničke postavke"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Upravljajte korisnicima"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zatvori"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Povezano"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Isključiti prijenos podataka na mobilnoj mreži?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup podacima ni internetu putem mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem WiFi-ja."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vaš operater"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Postavke ne mogu potvrditi vaš odgovor jer aplikacija zaklanja zahtjev za odobrenje."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Dozvoliti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Može čitati informacije iz aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Postavke prozora povećala"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite da otvorite funkcije pristupačnosti. Prilagodite ili zamijenite dugme u Postavkama.\n\n"<annotation id="link">"Postavke"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Premjestite dugme do ivice da ga privremeno sakrijete"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pomjeranje gore lijevo"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pomjeranje gore desno"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Pomjeranje dolje lijevo"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Pomjeranje dolje desno"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Pomjeranje do ivice i sakrivanje"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Pomjeranje izvan ivice i prikaz"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aktiviranje/deaktiviranje"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Odaberite aplikaciju da dodate kontrole"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Prijenos podataka se neće automatski povezati"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani s mrežom"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Druge mreže nisu dostupne"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index daa63e6..b61ee2c 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversió de colors"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correcció de color"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuració d\'usuari"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Tanca"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Connectat"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vols desactivar les dades mòbils?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"No tindràs accés a dades ni a Internet mitjançant <xliff:g id="CARRIER">%s</xliff:g>. Internet només estarà disponible per Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"el teu operador de telefonia mòbil"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Com que hi ha una aplicació que oculta una sol·licitud de permís, no es pot verificar la teva resposta des de la configuració."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Vols permetre que <xliff:g id="APP_0">%1$s</xliff:g> mostri porcions de l\'aplicació <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Pot llegir informació de l\'aplicació <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configuració de la finestra de la lupa"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toca per obrir funcions d\'accessibilitat. Personalitza o substitueix el botó a Configuració.\n\n"<annotation id="link">"Mostra"</annotation>"."</string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mou el botó a l\'extrem per amagar-lo temporalment"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mou a dalt a l\'esquerra"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mou a dalt a la dreta"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mou a baix a l\'esquerra"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mou a baix a la dreta"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mou dins de les vores i amaga"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mou fora de les vores i mostra"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"commuta"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controls de dispositius"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Selecciona l\'aplicació per afegir controls"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Les dades mòbils no es connectaran automàticament"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sense connexió"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hi ha cap altra xarxa disponible"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index cba9448..ee36468 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Převrácení barev"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekce barev"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Uživatelské nastavení"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zavřít"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Připojeno"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vypnout mobilní data?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím <xliff:g id="CARRIER">%s</xliff:g> nebudete moci používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vašeho operátora"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Žádost o oprávnění je blokována jinou aplikací. Nastavení proto vaši odpověď nemůže ověřit."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Povolit aplikaci <xliff:g id="APP_0">%1$s</xliff:g> zobrazovat ukázky z aplikace <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Může číst informace z aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Nastavení okna zvětšení"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Klepnutím otevřete funkce přístupnosti. Tlačítko lze upravit nebo nahradit v Nastavení.\n\n"<annotation id="link">"Nastavení"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Přesunutím tlačítka k okraji ho dočasně skryjete"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Přesunout vlevo nahoru"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Přesunout vpravo nahoru"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Přesunout vlevo dolů"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Přesunout vpravo dolů"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Přesunout k okraji a skrýt"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Přesunout okraj ven a zobrazit"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"přepnout"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Ovládání zařízení"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Vyberte aplikaci, pro kterou chcete přidat ovládací prvky"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilní data se nebudou připojovat automaticky"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Žádné připojení"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Žádné další sítě nejsou k dispozici"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index a879cbb..220014e 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ombytning af farver"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farvekorrigering"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Brugerindstillinger"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrer brugere"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Udfør"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Luk"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Tilsluttet"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vil du deaktivere mobildata?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Du vil ikke have data- eller internetadgang via <xliff:g id="CARRIER">%s</xliff:g>. Der vil kun være adgang til internettet via Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"dit mobilselskab"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Indstillinger kan ikke bekræfte dit svar, da en app dækker for en anmodning om tilladelse."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Vil du give <xliff:g id="APP_0">%1$s</xliff:g> tilladelse til at vise eksempler fra <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Den kan læse oplysninger fra <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Indstillinger for lupvindue"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tryk for at åbne hjælpefunktioner. Tilpas eller erstat denne knap i Indstillinger.\n\n"<annotation id="link">"Se indstillingerne"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flyt knappen til kanten for at skjule den midlertidigt"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flyt op til venstre"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flyt op til højre"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Flyt ned til venstre"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Flyt ned til højre"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Flyt ud til kanten, og skjul"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Flyt ud til kanten, og vis"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"slå til/fra"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Enhedsstyring"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Vælg en app for at tilføje styring"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Ingen automatisk mobildataforbindelse"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Der er ingen forbindelse"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Der er ingen andre tilgængelige netværk"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index fb135ff..0b3af67 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helligkeit"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Farbumkehr"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farbkorrektur"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Nutzereinstellungen"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Fertig"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Schließen"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Verbunden"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Mobile Daten deaktivieren?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Du kannst dann nicht mehr über <xliff:g id="CARRIER">%s</xliff:g> auf Daten und das Internet zugreifen. Das Internet ist nur noch über WLAN verfügbar."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"deinen Mobilfunkanbieter"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Deine Eingabe wird von \"Einstellungen\" nicht erkannt, weil die Berechtigungsanfrage von einer App verdeckt wird."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> erlauben, Teile von <xliff:g id="APP_2">%2$s</xliff:g> anzuzeigen?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Darf Informationen aus <xliff:g id="APP">%1$s</xliff:g> lesen"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Einstellungen für das Vergrößerungsfenster"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tippe, um die Bedienungshilfen aufzurufen. Du kannst diese Schaltfläche in den Einstellungen anpassen oder ersetzen.\n\n"<annotation id="link">"Zu den Einstellungen"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Durch Ziehen an den Rand wird die Schaltfläche zeitweise ausgeblendet"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Nach oben links verschieben"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Nach rechts oben verschieben"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Nach unten links verschieben"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Nach unten rechts verschieben"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"An den Rand verschieben und verbergen"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Vom Rand verschieben und anzeigen"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Wechseln"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Gerätesteuerung"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"App zum Hinzufügen von Steuerelementen auswählen"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Keine automatische Verbindung über mobile Daten"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Keine Verbindung"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Keine anderen Netzwerke verfügbar"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 3d15953..d707e3e 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Φωτεινότητα"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Αντιστροφή χρωμάτων"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Διόρθωση χρωμάτων"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ρυθμίσεις χρήστη"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Διαχείριση χρηστών"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Τέλος"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Κλείσιμο"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Συνδέθηκε"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Απενεργοποίηση δεδομένων κινητής τηλεφωνίας;"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Δεν θα έχετε πρόσβαση σε δεδομένα ή στο διαδίκτυο μέσω της εταιρείας κινητής τηλεφωνίας <xliff:g id="CARRIER">%s</xliff:g>. Θα έχετε πρόσβαση στο διαδίκτυο μόνο μέσω Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"η εταιρεία κινητής τηλεφωνίας"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Επειδή μια εφαρμογή αποκρύπτει ένα αίτημα άδειας, δεν είναι δυνατή η επαλήθευση της απάντησής σας από τις Ρυθμίσεις."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Να επιτρέπεται στο <xliff:g id="APP_0">%1$s</xliff:g> να εμφανίζει τμήματα της εφαρμογής <xliff:g id="APP_2">%2$s</xliff:g>;"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Μπορεί να διαβάζει πληροφορίες από την εφαρμογή <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Ρυθμίσεις παραθύρου μεγεθυντικού φακού"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Πατήστε για άνοιγμα των λειτουργιών προσβασιμότητας. Προσαρμόστε ή αντικαταστήστε το κουμπί στις Ρυθμίσεις.\n\n"<annotation id="link">"Προβολή ρυθμίσεων"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Μετακινήστε το κουμπί στο άκρο για προσωρινή απόκρυψη"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Μετακίνηση επάνω αριστερά"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Μετακίνηση επάνω δεξιά"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Μετακίνηση κάτω αριστερά"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Μετακίνηση κάτω δεξιά"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Μετακίν. στο άκρο και απόκρυψη"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Μετακ. εκτός άκρου και εμφάν."</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"εναλλαγή"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Στοιχεία ελέγχου συσκευής"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Επιλογή εφαρμογής για προσθήκη στοιχείων ελέγχου"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Χωρίς αυτόματη σύνδεση δεδομένων κινητ. τηλεφωνίας"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Χωρίς σύνδεση"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Δεν υπάρχουν άλλα διαθέσιμα δίκτυα"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index e7166ac..3aaa5f5 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Manage users"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Close"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Connected"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Turn off mobile data?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"You won\'t have access to data or the Internet through <xliff:g id="CARRIER">%s</xliff:g>. Internet will only be available via Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"your operator"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Because an app is obscuring a permission request, Settings can’t verify your response."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Move bottom left"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Move bottom right"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Move to edge and hide"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Move out edge and show"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobile data won\'t auto‑connect"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index fe746d9..fb56d9f 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Manage users"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Close"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Connected"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Turn off mobile data?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"You won\'t have access to data or the Internet through <xliff:g id="CARRIER">%s</xliff:g>. Internet will only be available via Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"your carrier"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Because an app is obscuring a permission request, Settings can’t verify your response."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Move bottom left"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Move bottom right"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Move to edge and hide"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Move out edge and show"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobile data won\'t auto‑connect"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index e7166ac..3aaa5f5 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Manage users"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Close"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Connected"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Turn off mobile data?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"You won\'t have access to data or the Internet through <xliff:g id="CARRIER">%s</xliff:g>. Internet will only be available via Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"your operator"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Because an app is obscuring a permission request, Settings can’t verify your response."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Move bottom left"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Move bottom right"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Move to edge and hide"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Move out edge and show"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobile data won\'t auto‑connect"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index e7166ac..3aaa5f5 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"User settings"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Manage users"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Close"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Connected"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Turn off mobile data?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"You won\'t have access to data or the Internet through <xliff:g id="CARRIER">%s</xliff:g>. Internet will only be available via Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"your operator"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Because an app is obscuring a permission request, Settings can’t verify your response."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Magnifier window settings"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tap to open accessibility features. Customise or replace this button in Settings.\n\n"<annotation id="link">"View settings"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Move bottom left"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Move bottom right"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Move to edge and hide"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Move out edge and show"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobile data won\'t auto‑connect"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index b55b744..ee56838 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎Brightness‎‏‎‎‏‎"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎Color inversion‎‏‎‎‏‎"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‎Color correction‎‏‎‎‏‎"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎User settings‎‏‎‎‏‎"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‎Manage users‎‏‎‎‏‎"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎Done‎‏‎‎‏‎"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‎‏‎Close‎‏‎‎‏‎"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎Connected‎‏‎‎‏‎"</string>
@@ -727,6 +727,10 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‏‏‏‏‏‎‎Turn off mobile data?‎‏‎‎‏‎"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎You wont have access to data or the internet through ‎‏‎‎‏‏‎<xliff:g id="CARRIER">%s</xliff:g>‎‏‎‎‏‏‏‎. Internet will only be available via Wi-Fi.‎‏‎‎‏‎"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‎your carrier‎‏‎‎‏‎"</string>
+    <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎Switch back to ‎‏‎‎‏‏‎<xliff:g id="CARRIER">%s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎Mobile data wont automatically switch based on availability‎‏‎‎‏‎"</string>
+    <string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎No thanks‎‏‎‎‏‎"</string>
+    <string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎Yes, switch‎‏‎‎‏‎"</string>
     <string name="touch_filtered_warning" msgid="8119511393338714836">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‎‎‎Because an app is obscuring a permission request, Settings can’t verify your response.‎‏‎‎‏‎"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎‎‏‎Allow ‎‏‎‎‏‏‎<xliff:g id="APP_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to show ‎‏‎‎‏‏‎<xliff:g id="APP_2">%2$s</xliff:g>‎‏‎‎‏‏‏‎ slices?‎‏‎‎‏‎"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‎- It can read information from ‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
@@ -785,12 +789,15 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎Magnifier window settings‎‏‎‎‏‎"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‏‎‎Tap to open accessibility features. Customize or replace this button in Settings.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎View settings‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎Move button to the edge to hide it temporarily‎‏‎‎‏‎"</string>
+    <string name="accessibility_floating_button_undo" msgid="511112888715708241">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‎‎‎‏‎Undo‎‏‎‎‏‎"</string>
+    <string name="accessibility_floating_button_undo_message_text" msgid="3044079592757099698">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎{label}‎‏‎‎‏‏‏‎ shortcut removed‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎# shortcuts removed‎‏‎‎‏‎}}"</string>
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎Move top left‎‏‎‎‏‎"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎Move top right‎‏‎‎‏‎"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎Move bottom left‎‏‎‎‏‎"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎Move bottom right‎‏‎‎‏‎"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‎‎‏‎‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎Move to edge and hide‎‏‎‎‏‎"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎Move out edge and show‎‏‎‎‏‎"</string>
+    <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‏‏‎Remove‎‏‎‎‏‎"</string>
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‎toggle‎‏‎‎‏‎"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎Device controls‎‏‎‎‏‎"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎Choose app to add controls‎‏‎‎‏‎"</string>
@@ -933,6 +940,8 @@
     <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>
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎Mobile data won\'t auto‑connect‎‏‎‎‏‎"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎No connection‎‏‎‎‏‎"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎No other networks available‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index e7a6cf1..6160caf 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corregir colores"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuración del usuario"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Listo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Cerrar"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Conectado"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"¿Deseas desactivar los datos móviles?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"No tendrás acceso a datos móviles ni a Internet a través de <xliff:g id="CARRIER">%s</xliff:g>. Solo podrás conectarte a Internet mediante Wi‑Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"tu proveedor"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Como una app está bloqueando una solicitud de permiso, Configuración no puede verificar tu respuesta."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"¿Permitir que <xliff:g id="APP_0">%1$s</xliff:g> muestre fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Puede leer información sobre <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configuración de la ventana de ampliación"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Presiona para abrir las funciones de accesibilidad. Personaliza o cambia botón en Config.\n\n"<annotation id="link">"Ver config"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mueve el botón hacia el borde para ocultarlo temporalmente"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover arriba a la izquierda"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover arriba a la derecha"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover abajo a la izquierda"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover abajo a la derecha"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover fuera de borde y ocultar"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mover fuera de borde y mostrar"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activar o desactivar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controles de dispositivos"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Elige la app para agregar los controles"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"No se conectarán automáticamente los datos móviles"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hay otras redes disponibles"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index e8218b0..ed9e7d9 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección de color"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ajustes de usuario"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Hecho"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Cerrar"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Conectado"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"¿Desactivar datos móviles?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"No tendrás acceso a datos móviles ni a Internet a través de <xliff:g id="CARRIER">%s</xliff:g>. Solo podrás conectarte a Internet mediante Wi‑Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"tu operador"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Una aplicación impide ver una solicitud de permiso, por lo que Ajustes no puede verificar tu respuesta."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"¿Permitir que <xliff:g id="APP_0">%1$s</xliff:g> muestre fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Puede leer información de <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configuración de la ventana de la lupa"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toca para abrir funciones de accesibilidad. Personaliza o sustituye este botón en Ajustes.\n\n"<annotation id="link">"Ver ajustes"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mueve el botón hacia el borde para ocultarlo temporalmente"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover arriba a la izquierda"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover arriba a la derecha"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover abajo a la izquierda"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover abajo a la derecha"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover al borde y ocultar"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mover al borde y mostrar"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activar/desactivar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Elige una aplicación para añadir controles"</string>
@@ -839,7 +854,7 @@
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> de <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
     <string name="controls_media_button_play" msgid="2705068099607410633">"Reproducir"</string>
     <string name="controls_media_button_pause" msgid="8614887780950376258">"Pausar"</string>
-    <string name="controls_media_button_prev" msgid="8126822360056482970">"Pista anterior"</string>
+    <string name="controls_media_button_prev" msgid="8126822360056482970">"Canción anterior"</string>
     <string name="controls_media_button_next" msgid="6662636627525947610">"Siguiente pista"</string>
     <string name="controls_media_button_connecting" msgid="3138354625847598095">"Conectando"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"Reproducir"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <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>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hay otras redes disponibles"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 11a9dc9..417c2c2 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Heledus"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Värvide ümberpööramine"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Värviparandus"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Kasutaja seaded"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Sule"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Ühendatud"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Kas lülitada mobiilne andmeside välja?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Pärast seda pole teil operaatori <xliff:g id="CARRIER">%s</xliff:g> kaudu juurdepääsu andmesidele ega internetile. Internet on saadaval ainult WiFi kaudu."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"teie operaator"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Seaded ei saa teie vastust kinnitada, sest rakendus varjab loataotlust."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Kas lubada rakendusel <xliff:g id="APP_0">%1$s</xliff:g> näidata rakenduse <xliff:g id="APP_2">%2$s</xliff:g> lõike?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- See saab lugeda teavet rakendusest <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Luubi akna seaded"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Puudutage juurdepääsufunktsioonide avamiseks. Kohandage nuppu või asendage see seadetes.\n\n"<annotation id="link">"Kuva seaded"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Teisaldage nupp serva, et see ajutiselt peita"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Teisalda üles vasakule"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Teisalda üles paremale"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Teisalda alla vasakule"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Teisalda alla paremale"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Teisalda serva ja kuva"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Teisalda servast eemale ja kuva"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"lülita"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Seadmete juhikud"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Valige juhtelementide lisamiseks rakendus"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobiilset andmesideühendust ei looda automaatselt"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ühendus puudub"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ühtegi muud võrku pole saadaval"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 3714781..ee1ba23 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Distira"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kolore-alderantzikatzea"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Koloreen zuzenketa"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Erabiltzaile-ezarpenak"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Eginda"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Itxi"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Konektatuta"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Datu-konexioa desaktibatu nahi duzu?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> erabilita ezingo dituzu erabili datuak edo Internet. Wifi-sare baten bidez soilik konektatu ahal izango zara Internetera."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"Zure operadorea"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Aplikazio bat baimen-eskaera oztopatzen ari denez, ezarpenek ezin dute egiaztatu erantzuna."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> aplikazioaren zatiak erakusteko baimena eman nahi diozu <xliff:g id="APP_0">%1$s</xliff:g> aplikazioari?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g> aplikazioaren informazioa irakur dezake."</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Luparen leihoaren ezarpenak"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Erabilerraztasun-eginbideak irekitzeko, sakatu hau. Ezarpenetan pertsonalizatu edo ordez dezakezu botoia.\n\n"<annotation id="link">"Ikusi ezarpenak"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Eraman botoia ertzera aldi baterako ezkutatzeko"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Eraman goialdera, ezkerretara"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Eraman goialdera, eskuinetara"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Eraman behealdera, ezkerretara"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Eraman behealdera, eskuinetara"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Eraman ertzera eta ezkutatu"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Atera ertzetik eta erakutsi"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aldatu"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Gailuak kontrolatzeko widgetak"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Aukeratu aplikazio bat kontrolatzeko aukerak gehitzeko"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Ez da automatikoki aktibatuko datu-konexioa"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Konexiorik gabe"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ez dago beste sare erabilgarririk"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 183a9ed..62d73ae 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"روشنایی"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"وارونگی رنگ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"تصحیح رنگ"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"تنظیمات کاربر"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"تمام"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"بستن"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"متصل"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"داده تلفن همراه خاموش شود؟"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"‏نمی‌توانید ازطریق <xliff:g id="CARRIER">%s</xliff:g> به داده یا اینترنت دسترسی داشته باشید. اینترنت فقط ازطریق Wi-Fi در دسترس خواهد بود."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"شرکت مخابراتی شما"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"چون برنامه‌ای درحال ایجاد تداخل در درخواست مجوز است، «تنظیمات» نمی‌تواند پاسخ شما را تأیید کند."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"به <xliff:g id="APP_0">%1$s</xliff:g> اجازه داده شود تکه‌های <xliff:g id="APP_2">%2$s</xliff:g> را نشان دهد؟"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- می‌تواند اطلاعات <xliff:g id="APP">%1$s</xliff:g> را بخواند"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"تنظیمات پنجره ذره‌بین"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"برای باز کردن ویژگی‌های دسترس‌پذیری ضربه بزنید. در تنظیمات این دکمه را سفارشی یا جایگزین کنید\n\n"<annotation id="link">"تنظیمات"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"برای پنهان کردن موقتی دکمه، آن را به لبه ببرید"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"انتقال به بالا سمت راست"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"انتقال به بالا سمت چپ"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"انتقال به پایین سمت راست"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"انتقال به پایین سمت چپ"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"انتقال به لبه و پنهان کردن"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"انتقال به خارج از لبه و نمایش"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"روشن/ خاموش کردن"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"کنترل‌های دستگاه"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"انتخاب برنامه برای افزودن کنترل‌ها"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"داده تلفن همراه به‌طور خودکار متصل نخواهد شد"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"اتصال برقرار نیست"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"شبکه دیگری وجود ندارد"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index df05015..bfb450f 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kirkkaus"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Käänteiset värit"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Värinkorjaus"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Käyttäjäasetukset"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Sulje"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Yhdistetty"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Laitetaanko mobiilidata pois päältä?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> ei enää tarjoa pääsyä dataan eikä internetyhteyttä, joka on saatavilla vain Wi-Fin kautta."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operaattorisi"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Sovellus peittää käyttöoikeuspyynnön, joten Asetukset ei voi vahvistaa valintaasi."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Saako <xliff:g id="APP_0">%1$s</xliff:g> näyttää osia sovelluksesta <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Se voi lukea tietoja sovelluksesta <xliff:g id="APP">%1$s</xliff:g>."</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Ikkunan suurennuksen asetukset"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Avaa esteettömyysominaisuudet napauttamalla. Yksilöi tai vaihda painike asetuksista.\n\n"<annotation id="link">"Avaa asetukset"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Piilota painike tilapäisesti siirtämällä se reunaan"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Siirrä vasempaan yläreunaan"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Siirrä oikeaan yläreunaan"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Siirrä vasempaan alareunaan"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Siirrä oikeaan alareunaan"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Siirrä reunaan ja piilota"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Siirrä pois reunasta ja näytä"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"vaihda"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Laitehallinta"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Valitse sovellus lisätäksesi säätimiä"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobiilidata ei yhdisty automaattisesti"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ei yhteyttä"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ei muita verkkoja käytettävissä"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index c5c941b..b2d925c 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correction des couleurs"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Paramètres utilisateur"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Terminé"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fermer"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Connecté"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Désactiver les données cellulaires?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Vous n\'aurez pas accès aux données ni à Internet avec <xliff:g id="CARRIER">%s</xliff:g>. Vous ne pourrez accéder à Internet que par Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"votre fournisseur de services"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Une application obscurcit une demande d\'autorisation, alors Paramètres ne peut pas vérifier votre réponse."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Autoriser <xliff:g id="APP_0">%1$s</xliff:g> à afficher <xliff:g id="APP_2">%2$s</xliff:g> tranches?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Il peut lire de l\'information de <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Paramètres de la fenêtre de loupe"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Touchez pour ouvrir fonction. d\'access. Personnalisez ou remplacez bouton dans Param.\n\n"<annotation id="link">"Afficher param."</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Déplacez le bouton vers le bord pour le masquer temporairement"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer dans coin sup. gauche"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer dans coin sup. droit"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Déplacer dans coin inf. gauche"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Déplacer dans coin inf. droit"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Éloigner du bord et masquer"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Éloigner du bord et afficher"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"basculer"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'application pour laquelle ajouter des commandes"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Aucune connexion auto. des données cellulaires"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau n\'est accessible"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 1a2b877..12a88c6 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correction des couleurs"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Paramètres utilisateur"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"OK"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fermer"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Connecté"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Désactiver les données mobiles ?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Vous n\'aurez pas accès aux données mobiles ni à Internet via <xliff:g id="CARRIER">%s</xliff:g>. Vous ne pourrez accéder à Internet que par Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"votre opérateur"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"L\'application Paramètres ne peut pas valider votre réponse, car une application masque la demande d\'autorisation."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Autoriser <xliff:g id="APP_0">%1$s</xliff:g> à afficher des éléments de <xliff:g id="APP_2">%2$s</xliff:g> ?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Accès aux informations de <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Paramètres de la fenêtre d\'agrandissement"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Appuyez pour ouvrir fonctionnalités d\'accessibilité. Personnalisez ou remplacez bouton dans paramètres.\n\n"<annotation id="link">"Voir paramètres"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Déplacer le bouton vers le bord pour le masquer temporairement"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer en haut à gauche"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer en haut à droite"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Déplacer en bas à gauche"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Déplacer en bas à droite"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Rapprocher du bord et masquer"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Éloigner du bord et afficher"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activer/désactiver"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'appli pour laquelle ajouter des commandes"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Pas de connexion automatique des données mobiles"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau disponible"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index e5def1e..d18d596 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversión da cor"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección da cor"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuración de usuario"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Feito"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Pechar"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Conectado"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Queres desactivar os datos móbiles?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Non terás acceso aos datos nin a Internet a través de <xliff:g id="CARRIER">%s</xliff:g>. Internet só estará dispoñible mediante a wifi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"o teu operador"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Dado que unha aplicación se superpón sobre unha solicitude de permiso, a configuración non pode verificar a túa resposta."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Queres permitir que <xliff:g id="APP_0">%1$s</xliff:g> mostre fragmentos de aplicación de <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Pode ler información da aplicación <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configuración da ventá da lupa"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toca para abrir as funcións de accesibilidade. Cambia este botón en Configuración.\n\n"<annotation id="link">"Ver configuración"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Para ocultar temporalmente o botón, móveo ata o bordo"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover á parte super. esquerda"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover á parte superior dereita"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover á parte infer. esquerda"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover á parte inferior dereita"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover ao bordo e ocultar"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mover fóra do bordo e mostrar"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activar/desactivar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Escolle unha aplicación para engadir controis"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Os datos móbiles non se conectarán automaticamente"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sen conexión"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Non hai outras redes dispoñibles"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 552b049..034c161 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"તેજ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"વિપરીત રંગમાં બદલવું"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"રંગ સુધારણા"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"વપરાશકર્તા સેટિંગ"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"થઈ ગયું"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"બંધ કરો"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"કનેક્ટ થયેલું"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"મોબાઇલ ડેટા બંધ કરીએ?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"તમને <xliff:g id="CARRIER">%s</xliff:g> મારફતે ડેટા અથવા ઇન્ટરનેટનો ઍક્સેસ મળશે નહીં. ઇન્ટરનેટ માત્ર વાઇ-ફાઇ દ્વારા ઉપલબ્ધ થશે."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"તમારા કૅરિઅર"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"કોઈ ઍપ પરવાનગી વિનંતીને અસ્પષ્ટ કરતી હોવાને કારણે, સેટિંગ તમારા પ્રતિસાદને ચકાસી શકતું નથી."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g>ને <xliff:g id="APP_2">%2$s</xliff:g> સ્લાઇસ બતાવવાની મંજૂરી આપીએ?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- મારાથી <xliff:g id="APP">%1$s</xliff:g>ની માહિતી વાંચી શકાતી નથી"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"મેગ્નિફાયર વિન્ડોના સેટિંગ"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ઍક્સેસિબિલિટી સુવિધાઓ ખોલવા માટે ટૅપ કરો. સેટિંગમાં આ બટનને કસ્ટમાઇઝ કરો અથવા બદલો.\n\n"<annotation id="link">"સેટિંગ જુઓ"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"તેને હંગામી રૂપે ખસેડવા માટે બટનને કિનારી પર ખસેડો"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ઉપર ડાબે ખસેડો"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ઉપર જમણે ખસેડો"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"નીચે ડાબે ખસેડો"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"નીચે જમણે ખસેડો"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"કિનારી પર ખસેડો અને છુપાવો"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"કિનારીથી ખસેડો અને બતાવો"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ટૉગલ કરો"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ડિવાઇસનાં નિયંત્રણો"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"નિયંત્રણો ઉમેરવા માટે ઍપ પસંદ કરો"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"મોબાઇલ ડેટા ઑટોમૅટિક રીતે કનેક્ટ થશે નહીં"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"કોઈ કનેક્શન નથી"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"બીજાં કોઈ નેટવર્ક ઉપલબ્ધ નથી"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index c0052d6..dc94a8d 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"स्क्रीन की रोशनी"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"रंग बदलने की सुविधा"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रंग में सुधार करने की सुविधा"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"उपयोगकर्ता सेटिंग"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"हो गया"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"रद्द करें"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"कनेक्ट है"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा बंद करना चाहते हैं?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"आप <xliff:g id="CARRIER">%s</xliff:g> से डेटा या इंटरनेट का इस्तेमाल नहीं कर पाएंगे. इंटरनेट सिर्फ़ वाई-फ़ाई से चलेगा."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"आपको मोबाइल और इंटरनेट सेवा देने वाली कंपनी"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"ऐप की वजह से मंज़ूरी के अनुरोध को समझने में दिक्कत हो रही है, इसलिए सेटिंग से आपके जवाब की पुष्टि नहीं हो पा रही है."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> को <xliff:g id="APP_2">%2$s</xliff:g> के हिस्से (स्लाइस) दिखाने की मंज़ूरी दें?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- यह <xliff:g id="APP">%1$s</xliff:g> से सूचना पढ़ सकता है"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ज़ूम करने की सुविधा वाली विंडो से जुड़ी सेटिंग"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"सुलभता सुविधाएं खोलने के लिए टैप करें. सेटिंग में, इस बटन को बदलें या अपने हिसाब से सेट करें.\n\n"<annotation id="link">"सेटिंग देखें"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटन को कुछ समय छिपाने के लिए, उसे किनारे पर ले जाएं"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सबसे ऊपर बाईं ओर ले जाएं"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सबसे ऊपर दाईं ओर ले जाएं"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"सबसे नीचे बाईं ओर ले जाएं"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"सबसे नीचे दाईं ओर ले जाएं"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"एज पर ले जाएं और छिपाएं"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"एज से निकालें और दिखाएं"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"टॉगल करें"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"डिवाइस कंट्रोल"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"कंट्रोल जोड़ने के लिए ऐप्लिकेशन चुनें"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"मोबाइल डेटा अपने-आप कनेक्ट नहीं होगा"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"इंटरनेट कनेक्शन नहीं है"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"कोई दूसरा नेटवर्क उपलब्ध नहीं है"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 60f0c8a..be5253f 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Svjetlina"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcija boja"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisničke postavke"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Upravljajte korisnicima"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zatvori"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Povezano"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Isključiti mobilne podatke?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup mobilnim podacima ili internetu putem operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem Wi-Fija."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vaš mobilni operater"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Budući da aplikacija prekriva zahtjev za dopuštenje, Postavke ne mogu potvrditi vaš odgovor."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Želite li dopustiti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– može čitati informacije aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Postavke prozora povećala"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dodirnite za otvaranje značajki pristupačnosti. Prilagodite ili zamijenite taj gumb u postavkama.\n\n"<annotation id="link">"Pregledajte postavke"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pomaknite gumb do ruba da biste ga privremeno sakrili"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premjesti u gornji lijevi kut"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premjesti u gornji desni kut"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Premjesti u donji lijevi kut"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Premjesti u donji desni kut"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Premjesti na rub i sakrij"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Ukloni s ruba i prikaži"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"promijeni"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Odabir aplikacije za dodavanje kontrola"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilna veza neće se automatski uspostaviti"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 0f6daa2..cf07438 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Fényerő"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Színek invertálása"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Színjavítás"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Felhasználói beállítások"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Kész"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Bezárás"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Csatlakoztatva"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Kikapcsolja a mobiladatokat?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nem lesz adat-, illetve internet-hozzáférése a <xliff:g id="CARRIER">%s</xliff:g> szolgáltatón keresztül. Az internethez csak Wi-Fi-n keresztül csatlakozhat."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"saját mobilszolgáltató"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Mivel az egyik alkalmazás eltakarja az engedélykérést, a Beállítások alkalmazás nem tudja ellenőrizni az Ön válaszát."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Engedélyezi a(z) <xliff:g id="APP_0">%1$s</xliff:g> alkalmazásnak, hogy részleteket mutasson a(z) <xliff:g id="APP_2">%2$s</xliff:g> alkalmazásból?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Információkat olvashat a(z) <xliff:g id="APP">%1$s</xliff:g> alkalmazásból"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Nagyítóablak beállításai"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Koppintson a kisegítő lehetőségek megnyitásához. A gombot a Beállításokban módosíthatja.\n\n"<annotation id="link">"Beállítások"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"A gombot a szélre áthelyezve ideiglenesen elrejtheti"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Áthelyezés fel és balra"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Áthelyezés fel és jobbra"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Áthelyezés le és balra"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Áthelyezés le és jobbra"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Áthelyezés a szélen kívül és elrejtés"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Áthelyezés a szélen kívül és mutatás"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"váltás"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Eszközvezérlők"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Válasszon alkalmazást a vezérlők hozzáadásához"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Nincs automatikus mobiladat-kapcsolat"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nincs kapcsolat"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nincs több rendelkezésre álló hálózat"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 4724d5c..bfd051b 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Պայծառություն"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Գունաշրջում"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Գունաշտկում"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Օգտատիրոջ կարգավորումներ"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Պատրաստ է"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Փակել"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Միացված է"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Անջատե՞լ բջջային ինտերնետը"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> օպերատորի բջջային ինտերնետը հասանելի չի լինի: Համացանցից կկարողանաք  օգտվել միայն Wi-Fi-ի միջոցով:"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"Ձեր"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Քանի որ ներածումն արգելափակված է ինչ-որ հավելվածի կողմից, Կարգավորումները չեն կարող հաստատել ձեր պատասխանը:"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Թույլատրե՞լ <xliff:g id="APP_0">%1$s</xliff:g> հավելվածին ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Կարող է կարդալ տեղեկություններ <xliff:g id="APP">%1$s</xliff:g> հավելվածից"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Խոշորացույցի պատուհանի կարգավորումներ"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Հատուկ գործառույթները բացելու համար հպեք։ Անհատականացրեք այս կոճակը կարգավորումներում։\n\n"<annotation id="link">"Կարգավորումներ"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Կոճակը ժամանակավորապես թաքցնելու համար այն տեղափոխեք էկրանի եզր"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Տեղափոխել վերև՝ ձախ"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Տեղափոխել վերև՝ աջ"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Տեղափոխել ներքև՝ ձախ"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Տեղափոխել ներքև՝ աջ"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Տեղափոխել եզրից դուրս և թաքցնել"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Տեղափոխել եզրից դուրս և ցուցադրել"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"միացնել/անջատել"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Սարքերի կառավարման տարրեր"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Ընտրեք հավելված` կառավարման տարրեր ավելացնելու համար"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Բջջային ինտերնետն ավտոմատ չի միանա"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Կապ չկա"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Այլ հասանելի ցանցեր չկան"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 8fefe40..87496cf 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversi warna"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Koreksi warna"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Setelan pengguna"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Tutup"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Terhubung"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Nonaktifkan data seluler?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Anda tidak akan dapat mengakses data atau internet melalui <xliff:g id="CARRIER">%s</xliff:g>. Internet hanya akan tersedia melalui Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"Operator Seluler Anda"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Karena sebuah aplikasi menghalangi permintaan izin, Setelan tidak dapat memverifikasi respons Anda."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Izinkan <xliff:g id="APP_0">%1$s</xliff:g> menampilkan potongan <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Dapat membaca informasi dari <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Setelan jendela kaca pembesar"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ketuk untuk membuka fitur aksesibilitas. Sesuaikan atau ganti tombol ini di Setelan.\n\n"<annotation id="link">"Lihat setelan"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pindahkan tombol ke tepi agar tersembunyi untuk sementara"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pindahkan ke kiri atas"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pindahkan ke kanan atas"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Pindahkan ke kiri bawah"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Pindahkan ke kanan bawah"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Pindahkan ke tepi dan sembunyikan"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Pindahkan dari tepi dan tampilkan"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"alihkan"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrol perangkat"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Pilih aplikasi untuk menambahkan kontrol"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Data seluler tidak akan terhubung otomatis"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Tidak ada koneksi"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Jaringan lain tidak tersedia"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index d22680e..3d7e2ea 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Birtustig"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Umsnúningur lita"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Litaleiðrétting"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Notandastillingar"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Lokið"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Loka"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Tengt"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Viltu slökkva á farsímagögnum?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Þú munt ekki hafa aðgang að gögnum eða internetinu í gegnum <xliff:g id="CARRIER">%s</xliff:g>. Aðeins verður hægt að tengjast internetinu með Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"símafyrirtækið þitt"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Stillingar geta ekki staðfest svarið þitt vegna þess að forrit er að fela heimildarbeiðni."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Viltu leyfa <xliff:g id="APP_0">%1$s</xliff:g> að sýna sneiðar úr <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Það getur lesið upplýsingar úr <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Stillingar stækkunarglugga"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ýttu til að opna aðgengiseiginleika. Sérsníddu eða skiptu hnappinum út í stillingum.\n\n"<annotation id="link">"Skoða stillingar"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Færðu hnappinn að brúninni til að fela hann tímabundið"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Færa efst til vinstri"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Færa efst til hægri"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Færa neðst til vinstri"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Færa neðst til hægri"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Færa að jaðri og fela"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Færa að jaðri og birta"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"kveikja/slökkva"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Tækjastjórnun"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Veldu forrit til að bæta við stýringum"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Farsímagögn tengjast ekki sjálfkrafa"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Engin tenging"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Engin önnur net í boði"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index df6c14f..1115680 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosità"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversione dei colori"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correzione del colore"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Impostazioni utente"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Fine"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Chiudi"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Connesso"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Disattivare i dati mobili?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Non avrai accesso ai dati o a Internet tramite <xliff:g id="CARRIER">%s</xliff:g>. Internet sarà disponibile soltanto tramite Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"il tuo operatore"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Un\'app sta oscurando una richiesta di autorizzazione, pertanto Impostazioni non può verificare la tua risposta."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Vuoi consentire all\'app <xliff:g id="APP_0">%1$s</xliff:g> di mostrare porzioni dell\'app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Può leggere informazioni dell\'app <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Impostazioni della finestra di ingrandimento"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tocca per aprire funzioni di accessibilità. Personalizza o sostituisci il pulsante in Impostazioni.\n\n"<annotation id="link">"Vedi impostazioni"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Sposta il pulsante fino al bordo per nasconderlo temporaneamente"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <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>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Sposta in basso a destra"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Sposta fino a bordo e nascondi"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Sposta fuori da bordo e mostra"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"attiva/disattiva"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controllo dispositivi"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Scegli un\'app per aggiungere controlli"</string>
@@ -933,6 +948,10 @@
     <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">"Connessione attiva"</string>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Nessuna connessione dati mobili automatica"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nessuna connessione"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nessun\'altra rete disponibile"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index f24964a..1fe4e3e 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"בהירות"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"היפוך צבעים"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"תיקון צבע"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"הגדרות המשתמש"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ניהול משתמשים"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"סיום"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"סגירה"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"מחובר"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"לכבות את חבילת הגלישה?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"‏לא תהיה לך גישה לנתונים או לאינטרנט באמצעות <xliff:g id="CARRIER">%s</xliff:g>. אינטרנט יהיה זמין רק באמצעות Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"הספק שלך"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"יש אפליקציה שמסתירה את בקשת ההרשאה, ולכן אין אפשרות לאמת את התשובה בהגדרות."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"האם לאפשר ל-<xliff:g id="APP_0">%1$s</xliff:g> להציג חלקים מ-<xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- תהיה לה אפשרות לקרוא מידע מאפליקציית <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ההגדרות של חלון ההגדלה"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"מקישים כדי לפתוח את תכונות הנגישות. אפשר להחליף את הלחצן או להתאים אותו אישית בהגדרות.\n\n"<annotation id="link">"הצגת ההגדרות"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"כדי להסתיר זמנית את הלחצן, יש להזיז אותו לקצה"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"העברה לפינה השמאלית העליונה"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"העברה לפינה הימנית העליונה"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"העברה לפינה השמאלית התחתונה"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"העברה לפינה הימנית התחתונה"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"העברה לשוליים והסתרה"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"העברה מהשוליים והצגה"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"החלפת מצב"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"פקדי מכשירים"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"יש לבחור אפליקציה כדי להוסיף פקדים"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"החיבור לנתונים סלולריים לא מתבצע באופן אוטומטי"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"אין חיבור"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"אין רשתות זמינות אחרות"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7ea7223..b00ceb4 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"画面の明るさ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"色反転"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色補正"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ユーザー設定"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"完了"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"閉じる"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"接続済み"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"モバイルデータを OFF にしますか?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g>でデータやインターネットにアクセスできなくなります。インターネットには Wi-Fi からのみ接続できます。"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"携帯通信会社"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"アプリが許可リクエストを隠しているため、設定側でユーザーの応答を確認できません。"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"「<xliff:g id="APP_2">%2$s</xliff:g>」のスライスの表示を「<xliff:g id="APP_0">%1$s</xliff:g>」に許可しますか?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- 「<xliff:g id="APP">%1$s</xliff:g>」からの情報の読み取り"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"拡大鏡ウィンドウの設定"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"タップしてユーザー補助機能を開きます。ボタンのカスタマイズや入れ替えを [設定] で行えます。\n\n"<annotation id="link">"設定を表示"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ボタンを一時的に非表示にするには、端に移動させてください"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"左上に移動"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"右上に移動"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"左下に移動"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"右下に移動"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"端に移動して非表示"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"端から移動して表示"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"切り替え"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"デバイス コントロール"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"コントロールを追加するアプリの選択"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"モバイルデータには自動接続しません"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"接続なし"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"利用できるネットワークはありません"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 93c9d94..aab9a32 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"განათება"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ფერთა ინვერსია"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ფერთა კორექცია"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"მომხმარებლის პარამეტრები"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"დასრულდა"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"დახურვა"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"დაკავშირებულია"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"გსურთ მობილური ინტერნეტის გამორთვა?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"თქვენ არ გექნებათ მობილურ ინტერნეტზე ან ზოგადად ინტერნეტზე წვდომა <xliff:g id="CARRIER">%s</xliff:g>-ის მეშვეობით. ინტერნეტი მხოლოდ Wi-Fi-კავშირის მეშვეობით იქნება ხელმისაწვდომი."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"თქვენი ოპერატორი"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"ვინაიდან აპი ფარავს ნებართვის მოთხოვნას, პარამეტრების მიერ თქვენი პასუხი ვერ დასტურდება."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"ანიჭებთ ნებართვას <xliff:g id="APP_0">%1$s</xliff:g>-ს, აჩვენოს <xliff:g id="APP_2">%2$s</xliff:g>-ის ფრაგმენტები?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- მას შეუძლია ინფორმაციის <xliff:g id="APP">%1$s</xliff:g>-დან წაკითხვა"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"გადიდების ფანჯრის პარამეტრები"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"შეეხეთ მარტივი წვდომის ფუნქციების გასახსნელად. მოარგეთ ან შეცვალეთ ეს ღილაკი პარამეტრებში.\n\n"<annotation id="link">"პარამეტრების ნახვა"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"გადაიტანეთ ღილაკი კიდეში, რათა დროებით დამალოთ ის"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ზევით და მარცხნივ გადატანა"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ზევით და მარჯვნივ გადატანა"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ქვევით და მარცხნივ გადატანა"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ქვემოთ და მარჯვნივ გადატანა"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"კიდეში გადატანა და დამალვა"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"კიდეში გადატანა და გამოჩენა"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"გადართვა"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"მოწყობილ. მართვის საშუალებები"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"აირჩიეთ აპი მართვის საშუალებების დასამატებლად"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"მობილურ ინტერნეტს ავტომატურად არ დაუკავშირდება"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"კავშირი არ არის"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"სხვა ქსელები მიუწვდომელია"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index e096b7e..19bfdf5 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Жарықтығы"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Түс инверсиясы"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Түсті түзету"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Пайдаланушы параметрлері"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Дайын"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Жабу"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Қосылды"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Мобильдік интернет өшірілсін бе?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> операторы арқылы деректерге немесе интернетке кіре алмайсыз. Интернетке тек Wi-Fi арқылы кіресіз."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"операторыңыз"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Басқа қолданба рұқсат сұрауын жасырып тұрғандықтан, параметрлер жауабыңызды растай алмайды."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> қолданбасына <xliff:g id="APP_2">%2$s</xliff:g> қолданбасының үзінділерін көрсетуге рұқсат берілсін бе?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Бұл <xliff:g id="APP">%1$s</xliff:g> қолданбасындағы ақпаратты оқи алады"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Ұлғайтқыш терезесінің параметрлері"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Арнайы мүмкіндікті ашу үшін түртіңіз. Түймені параметрден реттеңіз не ауыстырыңыз.\n\n"<annotation id="link">"Параметрді көру"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Түймені уақытша жасыру үшін оны шетке қарай жылжытыңыз."</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Жоғарғы сол жаққа жылжыту"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Жоғарғы оң жаққа жылжыту"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Төменгі сол жаққа жылжыту"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Төменгі оң жаққа жылжыту"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Шетке жылжыту және жасыру"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Шетке жылжыту және көрсету"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ауыстыру"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Құрылғыны басқару элементтері"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Басқару элементтері қосылатын қолданбаны таңдаңыз"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Мобильдік интернет автоматты түрде қосылмайды."</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Байланыс жоқ"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Басқа қолжетімді желі жоқ"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index aa641e4..186244b 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ពន្លឺ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ការបញ្ច្រាស​ពណ៌"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ការ​កែតម្រូវ​ពណ៌"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ការកំណត់អ្នកប្រើប្រាស់"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"រួចរាល់"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"បិទ"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"បាន​ភ្ជាប់"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"បិទទិន្នន័យទូរសព្ទចល័ត?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"អ្នកនឹង​មិន​មាន​សិទ្ធិ​ចូល​ប្រើទិន្នន័យ​ ឬអ៊ីនធឺណិត​តាមរយៈ <xliff:g id="CARRIER">%s</xliff:g> បានឡើយ។ អ៊ីនធឺណិត​នឹងអាច​ប្រើបាន​តាមរយៈ Wi-Fi តែប៉ុណ្ណោះ។"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ក្រុមហ៊ុន​​សេវាទូរសព្ទរបស់អ្នក"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"ការកំណត់​មិនអាច​ផ្ទៀងផ្ទាត់​ការឆ្លើយតប​របស់អ្នក​បាន​ទេ ដោយសារ​កម្មវិធី​កំពុង​បាំងសំណើ​សុំការ​អនុញ្ញាត។"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"អនុញ្ញាតឱ្យ <xliff:g id="APP_0">%1$s</xliff:g> បង្ហាញ​ស្ថិតិប្រើប្រាស់​របស់ <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- វា​អាច​អាន​ព័ត៌មាន​ពី <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ការកំណត់វិនដូ​កម្មវិធីពង្រីក"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ចុចដើម្បីបើក​មុខងារ​ភាពងាយស្រួល។ ប្ដូរ ឬប្ដូរ​ប៊ូតុងនេះ​តាមបំណង​នៅក្នុង​ការកំណត់។\n\n"<annotation id="link">"មើល​ការកំណត់"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ផ្លាស់ទី​ប៊ូតុង​ទៅគែម ដើម្បីលាក់វា​ជាបណ្ដោះអាសន្ន"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ផ្លាស់ទីទៅខាងលើផ្នែកខាងឆ្វេង"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ផ្លាស់ទីទៅខាងលើផ្នែកខាងស្ដាំ"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ផ្លាស់ទីទៅខាងក្រោមផ្នែកខាងឆ្វេង​"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ផ្លាស់ទីទៅខាងក្រោមផ្នែកខាងស្ដាំ"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ផ្លាស់ទីទៅផ្នែកខាងចុង រួចលាក់"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ផ្លាស់ទីចេញពីផ្នែកខាងចុង រួចបង្ហាញ"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"បិទ/បើក"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ផ្ទាំងគ្រប់គ្រងឧបករណ៍"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"ជ្រើសរើស​កម្មវិធីដែលត្រូវបញ្ចូល​ផ្ទាំងគ្រប់គ្រង"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"ទិន្នន័យទូរសព្ទចល័ត​នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"មិនមាន​ការតភ្ជាប់ទេ"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"មិន​មាន​បណ្ដាញផ្សេងទៀតដែល​អាច​ប្រើ​បានទេ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index ab3f379..3341f8d 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ಪ್ರಕಾಶಮಾನ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ಕಲರ್ ಇನ್‍ವರ್ಶನ್"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ಬಣ್ಣದ ತಿದ್ದುಪಡಿ"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ಬಳಕೆದಾರರ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"ಮುಗಿದಿದೆ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ಮುಚ್ಚಿರಿ"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"ಮೊಬೈಲ್ ಡೇಟಾ ಆಫ್ ಮಾಡಬೇಕೆ?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"ನೀವು <xliff:g id="CARRIER">%s</xliff:g> ಮೂಲಕ ಡೇಟಾ ಅಥವಾ ಇಂಟರ್ನೆಟ್‌ಗೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುವುದಿಲ್ಲ. ಇಂಟರ್ನೆಟ್, ವೈ-ಫೈ ಮೂಲಕ ಮಾತ್ರ ಲಭ್ಯವಿರುತ್ತದೆ."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ನಿಮ್ಮ ವಾಹಕ"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"ಅನುಮತಿ ವಿನಂತಿಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಮರೆಮಾಚುತ್ತಿರುವ ಕಾರಣ, ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ನಿಮ್ಮ ಪ್ರತಿಕ್ರಿಯೆಯನ್ನು ಪರಿಶೀಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> ಸ್ಲೈಸ್‌ಗಳನ್ನು ತೋರಿಸಲು <xliff:g id="APP_0">%1$s</xliff:g> ಅನ್ನು ಅನುಮತಿಸುವುದೇ?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ಇದು <xliff:g id="APP">%1$s</xliff:g> ನಿಂದ ಮಾಹಿತಿಯನ್ನು ಓದಬಹುದು"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ಮ್ಯಾಗ್ನಿಫೈರ್ ವಿಂಡೋ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಈ ಬಟನ್ ಅನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ ಅಥವಾ ಬದಲಾಯಿಸಿ.\n\n"<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ಅದನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ಮರೆಮಾಡಲು ಅಂಚಿಗೆ ಬಟನ್ ಸರಿಸಿ"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ಎಡ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ಸ್ಕ್ರೀನ್‌ನ ಎಡ ಕೆಳಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ಕೆಳಗಿನ ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ಅಂಚಿಗೆ ಸರಿಸಿ ಮತ್ತು ಮರೆಮಾಡಿ"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ಅಂಚನ್ನು ಸರಿಸಿ ಮತ್ತು ತೋರಿಸಿ"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ಟಾಗಲ್ ಮಾಡಿ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ಸಾಧನ ನಿಯಂತ್ರಣಗಳು"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಲು ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"ಮೊಬೈಲ್ ಡೇಟಾ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗುವುದಿಲ್ಲ"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"ಯಾವುದೇ ಕನೆಕ್ಷನ್ ಇಲ್ಲ"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ಇತರ ಯಾವುದೇ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 11da089..783365c 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"밝기"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"색상 반전"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"색상 보정"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"사용자 설정"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"완료"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"닫기"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"연결됨"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"모바일 데이터를 사용 중지하시겠습니까?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g>을(를) 통해 데이터 또는 인터넷에 액세스할 수 없게 됩니다. 인터넷은 Wi-Fi를 통해서만 사용할 수 있습니다."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"이동통신사"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"앱이 권한 요청을 가리고 있기 때문에 설정에서 내 응답을 확인할 수 없습니다."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g>에서 <xliff:g id="APP_2">%2$s</xliff:g>의 슬라이스를 표시하도록 허용하시겠습니까?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g>의 정보를 읽을 수 있음"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"돋보기 창 설정"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"접근성 기능을 열려면 탭하세요. 설정에서 이 버튼을 맞춤설정하거나 교체할 수 있습니다.\n\n"<annotation id="link">"설정 보기"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"버튼을 가장자리로 옮겨서 일시적으로 숨기세요."</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"왼쪽 상단으로 이동"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"오른쪽 상단으로 이동"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"왼쪽 하단으로 이동"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"오른쪽 하단으로 이동"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"가장자리로 옮겨서 숨기기"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"가장자리 바깥으로 옮겨서 표시"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"전환"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"기기 컨트롤"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"컨트롤을 추가할 앱을 선택하세요"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"모바일 데이터가 자동으로 연결되지 않음"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"연결되지 않음"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"사용 가능한 다른 네트워크가 없음"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 676076c..b9da9f0 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Жарыктыгы"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Түстөрдү инверсиялоо"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Түстөрдү тууралоо"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Колдонуучунун параметрлери"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Бүттү"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Жабуу"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Туташкан"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Мобилдик Интернетти өчүрөсүзбү?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> байланыш оператору аркылуу Интернетке кире албай каласыз. Интернетке Wi-Fi аркылуу гана кирүүгө болот."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"байланыш операторуңуз"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Уруксат берүү сурамыңыз көрүнбөй калгандыктан, Жөндөөлөр жообуңузду ырастай албай жатат."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> колдонмосуна <xliff:g id="APP_2">%2$s</xliff:g> үлгүлөрүн көрсөтүүгө уруксат берилсинби?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g> колдонмосунун маалыматын окуйт"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Чоңойткуч терезесинин параметрлери"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Атайын мүмкүнчүлүктөрдү ачуу үчүн басыңыз. Бул баскычты Жөндөөлөрдөн өзгөртүңүз.\n\n"<annotation id="link">"Жөндөөлөрдү көрүү"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Баскычты убактылуу жашыра туруу үчүн экрандын четине жылдырыңыз"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Жогорку сол жакка жылдыруу"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Жогорку оң жакка жылдырыңыз"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Төмөнкү сол жакка жылдыруу"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Төмөнкү оң жакка жылдырыңыз"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Ичине жылдырып, көрсөтүңүз"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Сыртка жылдырып, көрсөтүңүз"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"өчүрүү/күйгүзүү"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Түзмөктү башкаруу элементтери"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Башкаруу элементтери кошула турган колдонмону тандоо"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Мобилдик трафик автоматтык түрдө туташтырылбайт"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Байланыш жок"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Башка тармактар жеткиликсиз"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 4fc25e2..d7dbb86 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ຄວາມແຈ້ງ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ການປີ້ນສີ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ການແກ້ໄຂສີ"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ຕັ້ງຄ່າຜູ້ໃຊ້"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"ແລ້ວໆ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ປິດ"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"ເຊື່ອມ​ຕໍ່ແລ້ວ"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"ປິດອິນເຕີເນັດມືຖືໄວ້ບໍ?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"ທ່ານຈະບໍ່ມີສິດເຂົ້າເຖິງຂໍ້ມູນ ຫຼື ອິນເຕີເນັດຜ່ານ <xliff:g id="CARRIER">%s</xliff:g>. ອິນເຕີເນັດຈະສາມາດໃຊ້ໄດ້ຜ່ານ Wi-Fi ເທົ່ານັ້ນ."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ຜູ້​ໃຫ້​ບໍ​ລິ​ການ​ຂອງ​ທ່ານ"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"ເນື່ອງຈາກມີແອັບໃດໜຶ່ງກຳລັງຂັດຂວາງການຂໍອະນຸຍາດ, ການຕັ້ງຄ່າຈຶ່ງບໍ່ສາມາດຢັ້ງຢືນການຕອບຮັບຂອງທ່ານໄດ້."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"ອະນຸຍາດ <xliff:g id="APP_0">%1$s</xliff:g> ໃຫ້ສະແດງ <xliff:g id="APP_2">%2$s</xliff:g> ສະໄລ້ບໍ?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ມັນສາມາດອ່ານຂໍ້ມູນຈາກ <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ການຕັ້ງຄ່າໜ້າຈໍຂະຫຍາຍ"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ແຕະເພື່ອເປີດຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ປັບແຕ່ງ ຫຼື ປ່ຽນປຸ່ມນີ້ໃນການຕັ້ງຄ່າ.\n\n"<annotation id="link">"ເບິ່ງການຕັ້ງຄ່າ"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ຍ້າຍປຸ່ມໄປໃສ່ຂອບເພື່ອເຊື່ອງມັນຊົ່ວຄາວ"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ຍ້າຍຊ້າຍເທິງ"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ຍ້າຍຂວາເທິງ"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ຍ້າຍຊ້າຍລຸ່ມ"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ຍ້າຍຂວາລຸ່ມ"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ຍ້າຍອອກຂອບ ແລະ ເຊື່ອງ"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ຍ້າຍອອກຂອບ ແລະ ສະແດງ"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ສະຫຼັບ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ການຄວບຄຸມອຸປະກອນ"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"ເລືອກແອັບເພື່ອເພີ່ມການຄວບຄຸມ"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"ຈະບໍ່ເຊື່ອມຕໍ່ອິນເຕີເນັດມືຖືອັດຕະໂນມັດ"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"ບໍ່ມີການເຊື່ອມຕໍ່"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ບໍ່ມີເຄືອຂ່າຍອື່ນທີ່ສາມາດໃຊ້ໄດ້"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index b9d6f33..e631ce2 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Šviesumas"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Spalvų inversija"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Spalvų taisymas"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Naudotojo nustatymai"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Atlikta"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Uždaryti"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Prijungtas"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Išjungti mobiliojo ryšio duomenis?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Naudodamiesi „<xliff:g id="CARRIER">%s</xliff:g>“ paslaugomis neturėsite galimybės pasiekti duomenų arba interneto. Internetą galėsite naudoti tik prisijungę prie „Wi-Fi“."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"savo operatoriaus"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Kadangi programa užstoja leidimo užklausą, nustatymuose negalima patvirtinti jūsų atsakymo."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Leisti „<xliff:g id="APP_0">%1$s</xliff:g>“ rodyti „<xliff:g id="APP_2">%2$s</xliff:g>“ fragmentus?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Gali nuskaityti informaciją iš „<xliff:g id="APP">%1$s</xliff:g>“"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Didinimo lango nustatymai"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Palietę atidarykite pritaikymo neįgaliesiems funkcijas. Tinkinkite arba pakeiskite šį mygtuką nustatymuose.\n\n"<annotation id="link">"Žr. nustatymus"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Perkelkite mygtuką prie krašto, kad laikinai jį paslėptumėte"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Perkelti į viršų kairėje"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Perkelti į viršų dešinėje"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Perkelti į apačią kairėje"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Perkelti į apačią dešinėje"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Perkelti į kraštą ir slėpti"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Perkelti iš krašto ir rodyti"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"perjungti"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Įrenginio valdikliai"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Pasirinkite programą, kad pridėtumėte valdiklių"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Naud. mob. r. duomenis nebus autom. prisijungiama"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nėra ryšio"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nėra kitų pasiekiamų tinklų"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 882ff7c..0a08654 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Spilgtums"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Krāsu inversija"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Krāsu korekcija"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Lietotāja iestatījumi"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Gatavs"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Aizvērt"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Pievienota"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vai izslēgt mobilos datus?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Izmantojot mobilo sakaru operatora <xliff:g id="CARRIER">%s</xliff:g> pakalpojumus, nevarēsiet piekļūt datiem vai internetam. Internetam varēsiet piekļūt, tikai izmantojot Wi-Fi savienojumu."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"jūsu mobilo sakaru operators"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Lietotne Iestatījumi nevar verificēt jūsu atbildi, jo cita lietotne aizsedz atļaujas pieprasījumu."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Vai atļaut lietotnei <xliff:g id="APP_0">%1$s</xliff:g> rādīt lietotnes <xliff:g id="APP_2">%2$s</xliff:g> sadaļas?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Var lasīt informāciju no lietotnes <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Lupas loga iestatījumi"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Atveriet pieejamības funkcijas. Pielāgojiet vai aizstājiet šo pogu iestatījumos.\n\n"<annotation id="link">"Skatīt iestatījumus"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Lai īslaicīgi paslēptu pogu, pārvietojiet to uz malu"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pārvietot augšpusē pa kreisi"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pārvietot augšpusē pa labi"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Pārvietot apakšpusē pa kreisi"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Pārvietot apakšpusē pa labi"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Pārvietot uz malu un paslēpt"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Pārvietot no malas un parādīt"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"pārslēgt"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Ierīču vadīklas"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Izvēlieties lietotni, lai pievienotu vadīklas"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilo datu savienojums netiks veidots automātiski"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nav savienojuma"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nav pieejams neviens cits tīkls"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 0d98dc6..e714055 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветленост"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверзија на боите"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекција на боите"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Кориснички поставки"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Затвори"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Поврзано"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Да се исклучи мобилниот интернет?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Нема да имате пристап до податоците или интернетот преку <xliff:g id="CARRIER">%s</xliff:g>. Интернетот ќе биде достапен само преку Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"вашиот оператор"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Бидејќи апликацијата го прикрива барањето за дозвола, „Поставките“ не може да го потврдат вашиот одговор."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Да се дозволи <xliff:g id="APP_0">%1$s</xliff:g> да прикажува делови од <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Може да чита информации од <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Поставки за прозорец за лупа"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Допрете за функциите за пристапност. Приспособете или заменете го копчево во „Поставки“.\n\n"<annotation id="link">"Прикажи поставки"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Преместете го копчето до работ за да го сокриете привремено"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Премести горе лево"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Премести долу лево"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Премести долу десно"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Премести до работ и сокриј"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Премести над работ и прикажи"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"вклучување/исклучување"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Контроли за уредите"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Изберете апликација за да додадете контроли"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Мобилниот интернет не може да се поврзе автоматски"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Нема интернет-врска"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нема други достапни мрежи"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 868d759..666e601 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"തെളിച്ചം"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"നിറം വിപരീതമാക്കൽ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"നിറം ശരിയാക്കൽ"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ഉപയോക്തൃ ക്രമീകരണം"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ഉപയോക്താക്കളെ മാനേജ് ചെയ്യുക"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"പൂർത്തിയാക്കി"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"അടയ്ക്കുക"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"കണക്‌റ്റുചെയ്‌തു"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"മൊബൈൽ ഡാറ്റ ഓഫാക്കണോ?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"നിങ്ങൾക്ക് ഡാറ്റയിലേക്ക് ആക്‌സസോ അല്ലെങ്കിൽ <xliff:g id="CARRIER">%s</xliff:g> മുഖേനയുള്ള ഇന്റർനെറ്റോ ഉണ്ടാകില്ല. വൈഫൈ മുഖേന മാത്രമായിരിക്കും ഇന്റർനെറ്റ് ലഭ്യത."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"നിങ്ങളുടെ കാരിയർ"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"അനുമതി അഭ്യർത്ഥനയെ ഒരു ആപ്പ് മറയ്‌ക്കുന്നതിനാൽ, ക്രമീകരണത്തിന് നിങ്ങളുടെ പ്രതികരണം പരിശോധിച്ചുറപ്പിക്കാനാകില്ല."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> സ്ലൈസുകൾ കാണിക്കാൻ <xliff:g id="APP_0">%1$s</xliff:g>-നെ അനുവദിക്കണോ?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ഇതിന് <xliff:g id="APP">%1$s</xliff:g>-ൽ നിന്ന് വിവരങ്ങൾ വായിക്കാനാകും"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"മാഗ്നിഫയർ വിൻഡോ ക്രമീകരണം"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ഉപയോഗസഹായി ഫീച്ചർ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ക്രമീകരണത്തിൽ ഈ ബട്ടൺ ഇഷ്ടാനുസൃതമാക്കാം, മാറ്റാം.\n\n"<annotation id="link">"ക്രമീകരണം കാണൂ"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"തൽക്കാലം മറയ്‌ക്കുന്നതിന് ബട്ടൺ അരുകിലേക്ക് നീക്കുക"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"മുകളിൽ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ചുവടെ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ചുവടെ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"എഡ്‌ജിലേക്ക് നീക്കി മറയ്‌ക്കുക"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"എഡ്‌ജിൽ നിന്ന് നീക്കി കാണിക്കൂ"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"മാറ്റുക"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ഉപകരണ നിയന്ത്രണങ്ങൾ"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"നിയന്ത്രണങ്ങൾ ചേർക്കാൻ ആപ്പ് തിരഞ്ഞെടുക്കുക"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"മൊബൈൽ ഡാറ്റ സ്വയം കണക്റ്റ് ചെയ്യില്ല"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"കണക്ഷനില്ല"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"മറ്റ് നെറ്റ്‌വർക്കുകളൊന്നും ലഭ്യമല്ല"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 25929e6..769cdda 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Тодрол"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Өнгө хувиргалт"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Өнгө тохируулга"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Хэрэглэгчийн тохиргоо"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Хэрэглэгчдийг удирдах"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Дууссан"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Хаах"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Холбогдсон"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Мобайл датаг унтраах уу?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Та <xliff:g id="CARRIER">%s</xliff:g>-р дата эсвэл интернэтэд хандах боломжгүй болно. Интернэтэд зөвхөн Wi-Fi-р холбогдох боломжтой болно."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"таны оператор компани"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Апп нь зөвшөөрлийн хүсэлтийг танихгүй байгаа тул Тохиргооноос таны хариултыг баталгаажуулах боломжгүй байна."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g>-д <xliff:g id="APP_2">%2$s</xliff:g>-н хэсгүүдийг (slices) харуулахыг зөвшөөрөх үү?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Энэ нь <xliff:g id="APP">%1$s</xliff:g>-с мэдээлэл унших боломжтой"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Томруулагчийн цонхны тохиргоо"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Хандалтын онцлогуудыг нээхийн тулд товшино уу. Энэ товчлуурыг Тохиргоо хэсэгт өөрчилж эсвэл солиорой.\n\n"<annotation id="link">"Тохиргоог харах"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Үүнийг түр нуухын тулд товчлуурыг зах руу зөөнө үү"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Зүүн дээш зөөх"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Баруун дээш зөөх"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Зүүн доош зөөх"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Баруун доош зөөх"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Ирмэг рүү зөөж, нуух"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Ирмэгээс гаргаж, харуулах"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"асаах/унтраах"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Төхөөрөмжийн хяналт"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Хяналтууд нэмэхийн тулд аппыг сонгоно уу"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Мобайл дата автоматаар холбогдохгүй"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Холболт алга"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Өөр боломжтой сүлжээ байхгүй байна"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index c6e99a7..d45b1e5 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"चमक"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"कलर इन्व्हर्जन"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रंग सुधारणा"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"वापरकर्ता सेटिंग्ज"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"पूर्ण झाले"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"बंद करा"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"कनेक्ट केलेले"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा बंद करायचा?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"तुम्हाला <xliff:g id="CARRIER">%s</xliff:g> मधून डेटा किंवा इंटरनेटचा अ‍ॅक्सेस नसेल. इंटरनेट फक्त वाय-फाय मार्फत उपलब्ध असेल."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"तुमचा वाहक"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"अ‍ॅप परवानगी विनंती अस्पष्‍ट करत असल्‍याने, सेटिंग्ज तुमचा प्रतिसाद पडताळू शकत नाहीत."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> ला <xliff:g id="APP_2">%2$s</xliff:g> चे तुकडे दाखवण्याची अनुमती द्यायची का?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ते <xliff:g id="APP">%1$s</xliff:g> ची माहिती वाचू शकते"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"मॅग्निफायर विंडो सेटिंग्ज"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"अ‍ॅक्सेसिबिलिटी वैशिष्ट्ये उघडण्यासाठी, टॅप करा. सेटिंग्जमध्ये हे बटण कस्टमाइझ करा किंवा बदला.\n\n"<annotation id="link">"सेटिंग्ज पहा"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटण तात्पुरते लपवण्यासाठी ते कोपर्‍यामध्ये हलवा"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"वर डावीकडे हलवा"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"वर उजवीकडे हलवा"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"तळाशी डावीकडे हलवा"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"तळाशी उजवीकडे हलवा"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"एजवर हलवा आणि लपवा"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"एजवर हलवा आणि दाखवा"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"टॉगल करा"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"डिव्हाइस नियंत्रणे"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"नियंत्रणे जोडण्यासाठी ॲप निवडा"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"मोबाइल डेटा ऑटो-कनेक्ट होणार नाही"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"कोणतेही कनेक्शन नाही"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"इतर कोणतेही नेटवर्क उपलब्ध नाहीत"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 99349b157..9e43396 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Penyongsangan warna"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Pembetulan warna"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Tetapan pengguna"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Tutup"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Disambungkan"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Matikan data mudah alih?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Anda tidak akan mempunyai akses kepada data atau Internet melalui <xliff:g id="CARRIER">%s</xliff:g>. Internet hanya tersedia melaui Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"pembawa anda"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Oleh sebab apl melindungi permintaan kebenaran, Tetapan tidak dapat mengesahkan jawapan anda."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Benarkan <xliff:g id="APP_0">%1$s</xliff:g> menunjukkan <xliff:g id="APP_2">%2$s</xliff:g> hirisan?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Hos hirisan boleh membaca maklumat daripada <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Tetapan tetingkap penggadang"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ketik untuk membuka ciri kebolehaksesan. Sesuaikan/gantikan butang ini dalam Tetapan.\n\n"<annotation id="link">"Lihat tetapan"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Gerakkan butang ke tepi untuk disembunyikan buat sementara waktu"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Alihkan ke atas sebelah kiri"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Alihkan ke atas sebelah kanan"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Alihkan ke bawah sebelah kiri"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Alihkan ke bawah sebelah kanan"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Alihkan ke tepi dan sorokkan"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Alihkan ke tepi dan tunjukkan"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"togol"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kawalan peranti"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Pilih apl untuk menambahkan kawalan"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Data mudah alih tidak akan autosambung"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Tiada sambungan"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Tiada rangkaian lain yang tersedia"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index f30846a..8f404b8 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"အလင်းတောက်ပမှု"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"အရောင်ပြောင်းပြန်ပြုလုပ်ရန်"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"အရောင် အမှန်ပြင်ခြင်း"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"အသုံးပြုသူ ဆက်တင်များ"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"ပြီးပါပြီ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ပိတ်ရန်"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"ချိတ်ဆက်ထား"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"မိုဘိုင်းဒေတာ ပိတ်မလား။"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> မှတစ်ဆင့် ဒေတာ သို့မဟုတ် အင်တာနက်ကို မသုံးနိုင်ပါ။ Wi-Fi ဖြင့်သာ အင်တာနက် သုံးနိုင်သည်။"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"သင်၏ ဝန်ဆောင်မှုပေးသူ"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"အပလီကေးရှင်းတစ်ခုက ခွင့်ပြုချက်တောင်းခံမှုကို ပိတ်ထားသောကြောင့် ဆက်တင်များသည် သင်၏ လုပ်ဆောင်ကို တုံ့ပြန်နိုင်ခြင်းမရှိပါ။"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> အား <xliff:g id="APP_2">%2$s</xliff:g> ၏အချပ်များ ပြသခွင့်ပြုပါသလား။"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ၎င်းသည် <xliff:g id="APP">%1$s</xliff:g> မှ အချက်အလက်ကို ဖတ်နိုင်သည်"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"မှန်ဘီလူးဝင်းဒိုး ဆက်တင်များ"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်ရန် တို့ပါ။ ဆက်တင်များတွင် ဤခလုတ်ကို စိတ်ကြိုက်ပြင်ပါ (သို့) လဲပါ။\n\n"<annotation id="link">"ဆက်တင်များ ကြည့်ရန်"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ခလုတ်ကို ယာယီဝှက်ရန် အစွန်းသို့ရွှေ့ပါ"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ဘယ်ဘက်ထိပ်သို့ ရွှေ့ရန်"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ညာဘက်ထိပ်သို့ ရွှေ့ရန်"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ဘယ်ဘက်အောက်ခြေသို့ ရွှေ့ရန်"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ညာဘက်အောက်ခြေသို့ ရွှေ့ရန်"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"အစွန်းသို့ရွှေ့ပြီး ဝှက်ရန်"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"အစွန်းမှရွှေ့ပြီး ပြရန်"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ပြောင်းရန်"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"စက်ထိန်းစနစ်"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"ထိန်းချုပ်မှုများထည့်ရန် အက်ပ်ရွေးခြင်း"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"မိုဘိုင်းဒေတာ အော်တိုမချိတ်ပါ"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"ချိတ်ဆက်မှုမရှိပါ"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"အခြားကွန်ရက်များ မရှိပါ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 117c864..94b8e90 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Fargeinvertering"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Fargekorrigering"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Brukerinnstillinger"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Ferdig"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Lukk"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Tilkoblet"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vil du slå av mobildata?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Du får ikke tilgang til data eller internett via <xliff:g id="CARRIER">%s</xliff:g>. Internett er bare tilgjengelig via Wifi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatøren din"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Fordi en app skjuler tillatelsesforespørselen, kan ikke Innstillinger bekrefte svaret ditt."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Vil du tillate at <xliff:g id="APP_0">%1$s</xliff:g> viser <xliff:g id="APP_2">%2$s</xliff:g>-utsnitt?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Den kan lese informasjon fra <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Innstillinger for forstørringsvindu"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Trykk for å åpne tilgj.funksjoner. Tilpass eller bytt knappen i Innstillinger.\n\n"<annotation id="link">"Se innstillingene"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flytt knappen til kanten for å skjule den midlertidig"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flytt til øverst til venstre"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flytt til øverst til høyre"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Flytt til nederst til venstre"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Flytt til nederst til høyre"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Flytt til kanten og skjul"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Flytt ut kanten og vis"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"slå av/på"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyring"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Velg en app for å legge til kontroller"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobildata kobler ikke til automatisk"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ingen tilkobling"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ingen andre nettverk er tilgjengelige"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 3519715..35937a9 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"उज्यालपन"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"कलर इन्भर्सन"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"कलर करेक्सन"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"प्रयोगकर्तासम्बन्धी सेटिङ"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"भयो"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"बन्द गर्नुहोस्"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"जोडिएको"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा निष्क्रिय पार्ने हो?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"तपाईं <xliff:g id="CARRIER">%s</xliff:g> मार्फत डेटा वा इन्टरनेट प्रयोग गर्न सक्नुहुने छैन। Wi-Fi मार्फत मात्र इन्टरनेट उपलब्ध हुने छ।"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"तपाईंको सेवा प्रदायक"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"कुनै एपको कारणले अनुमतिसम्बन्धी अनुरोध बुझ्न गाह्रो भइरहेकोले सेटिङहरूले तपाईंको प्रतिक्रिया प्रमाणित गर्न सक्दैनन्।"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> लाई <xliff:g id="APP_2">%2$s</xliff:g> का स्लाइसहरू देखाउन अनुमति दिने हो?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- यसले <xliff:g id="APP">%1$s</xliff:g> बाट जानकारी पढ्न सक्छ"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"म्याग्निफायर विन्डोसम्बन्धी सेटिङ"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"सर्वसुलभता कायम गर्ने सुविधा खोल्न ट्याप गर्नुहोस्। सेटिङमा गई यो बटन कस्टमाइज गर्नुहोस् वा बदल्नुहोस्।\n\n"<annotation id="link">"सेटिङ हेर्नुहोस्"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"यो बटन केही बेर नदेखिने पार्न किनारातिर सार्नुहोस्"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सिरानको बायाँतिर सार्नुहोस्"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सिरानको दायाँतिर सार्नुहोस्"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"पुछारको बायाँतिर सार्नुहोस्"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"पुछारको दायाँतिर सार्नुहोस्"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"किनारामा सार्नुहोस् र नदेखिने पार्नु…"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"किनाराबाट सार्नुहोस् र देखिने पार्नु…"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"टगल गर्नुहोस्"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"डिभाइस नियन्त्रण गर्ने विजेटहरू"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"कन्ट्रोल थप्नु पर्ने एप छान्नुहोस्"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"मोबाइल डेटा स्वतः कनेक्ट हुँदैन"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"इन्टरनेट छैन"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"अन्य नेटवर्क उपलब्ध छैनन्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 3c85148..4de0331 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleurinversie"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Kleurcorrectie"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Gebruikersinstellingen"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Sluiten"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Verbonden"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Mobiele data uitzetten?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Je hebt dan geen toegang meer tot data of internet via <xliff:g id="CARRIER">%s</xliff:g>. Internet is alleen nog beschikbaar via wifi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"je provider"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Aangezien een app een rechtenverzoek afdekt, kan Instellingen je reactie niet verifiëren."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> toestaan om segmenten van <xliff:g id="APP_2">%2$s</xliff:g> te tonen?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Deze kan informatie lezen van <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Instellingen voor vergrotingsvenster"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tik voor toegankelijkheidsfuncties. Wijzig of vervang deze knop via Instellingen.\n\n"<annotation id="link">"Naar Instellingen"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Knop naar de rand verplaatsen om deze tijdelijk te verbergen"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Naar linksboven verplaatsen"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Naar rechtsboven verplaatsen"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Naar linksonder verplaatsen"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Naar rechtsonder verplaatsen"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Naar rand verplaatsen en verbergen"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Over rand verplaatsen en tonen"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"schakelen"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Apparaatbediening"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Kies de app waaraan je bedieningselementen wilt toevoegen"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobiele data maakt niet automatisch verbinding"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Geen verbinding"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Geen andere netwerken beschikbaar"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 1cb3436..16941b8 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ଉଜ୍ଜ୍ୱଳତା"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ରଙ୍ଗ ଇନଭାର୍ସନ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ଉପଯୋଗକର୍ତ୍ତା ସେଟିଂସ"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"ହୋଇଗଲା"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"ସଂଯୁକ୍ତ"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"ମୋବାଇଲ୍‌ ଡାଟା ବନ୍ଦ କରିବେ?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟରନେଟ୍‌କୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର  ଆକ୍ସେସ୍ ରହିବ ନାହିଁ। ଇଣ୍ଟରନେଟ୍‌ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ଆପଣଙ୍କ କେରିଅର୍"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"ଗୋଟିଏ ଆପ୍‍ ଏକ ଅନୁମତି ଅନୁରୋଧକୁ ଦେଖିବାରେ ବାଧା ଦେଉଥିବାରୁ, ସେଟିଙ୍ଗ ଆପଣଙ୍କ ଉତ୍ତରକୁ ଯାଞ୍ଚ କରିପାରିବ ନାହିଁ।"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> ସ୍ଲାଇସ୍‌କୁ ଦେଖାଇବା ପାଇଁ <xliff:g id="APP_0">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ଏହା <xliff:g id="APP">%1$s</xliff:g>ରୁ ସୂଚନାକୁ ପଢ଼ିପାରିବ"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ମ୍ୟାଗ୍ନିଫାୟର ୱିଣ୍ଡୋର ସେଟିଂସ"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ଆକ୍ସେସିବିଲିଟୀ ଫିଚର ଖୋଲିବାକୁ ଟାପ କରନ୍ତୁ। ସେଟିଂସରେ ଏହି ବଟନକୁ କଷ୍ଟମାଇଜ କର କିମ୍ବା ବଦଳାଅ।\n\n"<annotation id="link">"ସେଟିଂସ ଦେଖନ୍ତୁ"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ବଟନକୁ ଅସ୍ଥାୟୀ ଭାବେ ଲୁଚାଇବା ପାଇଁ ଏହାକୁ ଗୋଟିଏ ଧାରକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ଶୀର୍ଷ ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ଶୀର୍ଷ ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ନିମ୍ନ ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ନିମ୍ନ ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ଧାରକୁ ମୁଭ୍ କରି ଲୁଚାନ୍ତୁ"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ଧାର ବାହାରକୁ ମୁଭ୍ କରି ଦେଖାନ୍ତୁ"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ଟୋଗଲ୍ କରନ୍ତୁ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ଡିଭାଇସ୍ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ଯୋଗ କରିବାକୁ ଆପ୍ ବାଛନ୍ତୁ"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"ମୋବାଇଲ ଡାଟା ସ୍ୱତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"ସଂଯୋଗ ନାହିଁ"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ଅନ୍ୟ କୌଣସି ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 992ffd6..fce69ec 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ਚਮਕ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ਰੰਗ ਪਲਟਨਾ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ਰੰਗ ਸੁਧਾਈ"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ਵਰਤੋਂਕਾਰ ਸੈਟਿੰਗਾਂ"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"ਹੋ ਗਿਆ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ਬੰਦ ਕਰੋ"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"ਕਨੈਕਟ ਕੀਤਾ"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"ਕੀ ਮੋਬਾਈਲ ਡਾਟਾ ਬੰਦ ਕਰਨਾ ਹੈ?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"ਤੁਸੀਂ <xliff:g id="CARRIER">%s</xliff:g> ਰਾਹੀਂ ਡਾਟੇ ਜਾਂ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕਰ ਸਕੋਗੇ। ਇੰਟਰਨੈੱਟ ਸਿਰਫ਼ ਵਾਈ-ਫਾਈ ਰਾਹੀਂ ਉਪਲਬਧ ਹੋਵੇਗਾ।"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ਤੁਹਾਡਾ ਕੈਰੀਅਰ"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"ਕਿਸੇ ਐਪ ਵੱਲੋਂ ਇਜਾਜ਼ਤ ਬੇਨਤੀ ਨੂੰ ਢਕੇ ਜਾਣ ਕਾਰਨ ਸੈਟਿੰਗਾਂ ਤੁਹਾਡੇ ਜਵਾਬ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਕਰ ਸਕਦੀਆਂ।"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"ਕੀ <xliff:g id="APP_0">%1$s</xliff:g> ਨੂੰ <xliff:g id="APP_2">%2$s</xliff:g> ਦੇ ਹਿੱਸੇ ਦਿਖਾਉਣ ਦੇਣੇ ਹਨ?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ਇਹ <xliff:g id="APP">%1$s</xliff:g> ਵਿੱਚੋਂ ਜਾਣਕਾਰੀ ਪੜ੍ਹ ਸਕਦਾ ਹੈ"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"ਵੱਡਦਰਸ਼ੀ ਵਿੰਡੋ ਸੈਟਿੰਗਾਂ"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ। ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਇਹ ਬਟਨ ਵਿਉਂਤਬੱਧ ਕਰੋ ਜਾਂ ਬਦਲੋ।\n\n"<annotation id="link">"ਸੈਟਿੰਗਾਂ ਦੇਖੋ"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ਬਟਨ ਨੂੰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਲੁਕਾਉਣ ਲਈ ਕਿਨਾਰੇ \'ਤੇ ਲਿਜਾਓ"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ਉੱਪਰ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ਹੇਠਾਂ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ਹੇਠਾਂ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ਕਿਨਾਰੇ ਵਿੱਚ ਲਿਜਾ ਕੇ ਲੁਕਾਓ"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ਕਿਨਾਰੇ ਤੋਂ ਬਾਹਰ ਕੱਢ ਕੇ ਦਿਖਾਓ"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ਟੌਗਲ ਕਰੋ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ਡੀਵਾਈਸ ਕੰਟਰੋਲ"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਐਪ ਚੁਣੋ"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"ਮੋਬਾਈਲ ਡਾਟਾ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ਕੋਈ ਹੋਰ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index b706359..8787f36 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jasność"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Odwrócenie kolorów"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcja kolorów"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ustawienia użytkownika"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotowe"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zamknij"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Połączono"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Wyłączyć mobilną transmisję danych?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nie będziesz mieć dostępu do transmisji danych ani internetu w <xliff:g id="CARRIER">%s</xliff:g>. Internet będzie dostępny tylko przez Wi‑Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"Twój operator"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Aplikacja Ustawienia nie może zweryfikować Twojej odpowiedzi, ponieważ inna aplikacja zasłania prośbę o udzielenie uprawnień."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Zezwolić aplikacji <xliff:g id="APP_0">%1$s</xliff:g> na pokazywanie wycinków z aplikacji <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Może odczytywać informacje z aplikacji <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Ustawienia okna powiększania"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Kliknij, aby otworzyć ułatwienia dostępu. Dostosuj lub zmień ten przycisk w Ustawieniach.\n\n"<annotation id="link">"Wyświetl ustawienia"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Przesuń przycisk do krawędzi, aby ukryć go tymczasowo"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Przenieś w lewy górny róg"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Przenieś w prawy górny róg"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Przenieś w lewy dolny róg"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Przenieś w prawy dolny róg"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Przenieś do krawędzi i ukryj"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Przenieś poza krawędź i pokaż"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"przełącz"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Sterowanie urządzeniami"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Wybierz aplikację, do której chcesz dodać elementy sterujące"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilna transmisja danych nie połączy się automatycznie"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Brak połączenia"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Brak innych dostępnych sieci"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 78a6409..e6f0580 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção de cor"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Config. do usuário"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fechar"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Conectado"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Você não terá acesso a dados ou à Internet pela operadora <xliff:g id="CARRIER">%s</xliff:g>. A Internet só estará disponível via Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"sua operadora"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Como um app está ocultando uma solicitação de permissão, as configurações não podem verificar sua resposta."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Permitir que <xliff:g id="APP_0">%1$s</xliff:g> mostre partes do app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Pode ler informações do app <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configurações da janela de lupa"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir os recursos de acessibilidade. Personalize ou substitua o botão nas Configurações.\n\n"<annotation id="link">"Ver configurações"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a borda para ocultá-lo temporariamente"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover para o canto superior esquerdo"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover para o canto superior direito"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover para o canto inferior esquerdo"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover para o canto inferior direito"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover para a borda e ocultar"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mover para fora da borda e exibir"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"alternar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Escolha um app para adicionar controles"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Sem conexão automática com dados móveis"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem conexão"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 30233b3..6c07ce0 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção da cor"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Definições do utilizador"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gerir utilizadores"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fechar"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Ligado"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não terá acesso a dados ou à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet estará disponível apenas por Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"o seu operador"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Uma vez que uma app está a ocultar um pedido de autorização, as Definições não conseguem validar a sua resposta."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Permitir que a app <xliff:g id="APP_0">%1$s</xliff:g> mostre partes da app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Pode ler informações da app <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Definições da janela da lupa"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir funcionalidades de acessibilidade. Personal. ou substitua botão em Defin.\n\n"<annotation id="link">"Ver defin."</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a extremidade para o ocultar temporariamente"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover p/ parte sup. esquerda"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover parte superior direita"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover p/ parte infer. esquerda"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover parte inferior direita"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover p/ extremidade e ocultar"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Retirar extremidade e mostrar"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ativar/desativar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controlos de dispositivos"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Escolha uma app para adicionar controlos"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Sem ligação automática com dados móveis"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem ligação"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 78a6409..e6f0580 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção de cor"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Config. do usuário"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fechar"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Conectado"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Você não terá acesso a dados ou à Internet pela operadora <xliff:g id="CARRIER">%s</xliff:g>. A Internet só estará disponível via Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"sua operadora"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Como um app está ocultando uma solicitação de permissão, as configurações não podem verificar sua resposta."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Permitir que <xliff:g id="APP_0">%1$s</xliff:g> mostre partes do app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Pode ler informações do app <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Configurações da janela de lupa"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Toque para abrir os recursos de acessibilidade. Personalize ou substitua o botão nas Configurações.\n\n"<annotation id="link">"Ver configurações"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a borda para ocultá-lo temporariamente"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover para o canto superior esquerdo"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover para o canto superior direito"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mover para o canto inferior esquerdo"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mover para o canto inferior direito"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mover para a borda e ocultar"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mover para fora da borda e exibir"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"alternar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Escolha um app para adicionar controles"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Sem conexão automática com dados móveis"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem conexão"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 0d96062..080edfd 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminozitate"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversarea culorilor"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corecția culorii"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Setări de utilizator"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Terminat"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Închide"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Conectat"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Dezactivezi datele mobile?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nu vei avea acces la date sau la internet prin intermediul <xliff:g id="CARRIER">%s</xliff:g>. Internetul va fi disponibil numai prin Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatorul tău"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Deoarece o aplicație acoperă o solicitare de permisiune, Setările nu îți pot verifica răspunsul."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Permiți ca <xliff:g id="APP_0">%1$s</xliff:g> să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Poate citi informații din <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Setările ferestrei de mărire"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Atinge ca să deschizi funcțiile de accesibilitate. Personalizează sau înlocuiește butonul în setări.\n\n"<annotation id="link">"Vezi setările"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mută butonul spre margine pentru a-l ascunde temporar"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mută în stânga sus"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mută în dreapta sus"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mută în stânga jos"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mută în dreapta jos"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mută la margine și ascunde"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mută de la margine și afișează"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Activează / dezactivează"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Comenzile dispozitivelor"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Alege aplicația pentru a adăuga comenzi"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Nu se conectează automat la date mobile"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nicio conexiune"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nu sunt disponibile alte rețele"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 3d13b2c..b42701e 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркость"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверсия цветов"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Коррекция цвета"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Пользовательские настройки"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Закрыть"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Подключено"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Отключить мобильный Интернет?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Вы не сможете передавать данные или выходить в Интернет через оператора \"<xliff:g id="CARRIER">%s</xliff:g>\". Интернет будет доступен только по сети Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ваш оператор"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Невозможно принять ваше согласие, поскольку запрос скрыт другим приложением."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Разрешить приложению \"<xliff:g id="APP_0">%1$s</xliff:g>\" показывать фрагменты приложения \"<xliff:g id="APP_2">%2$s</xliff:g>\"?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Ему станут доступны данные из приложения \"<xliff:g id="APP">%1$s</xliff:g>\"."</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Настройка окна лупы"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Нажмите, чтобы открыть спец. возможности. Настройте или замените эту кнопку в настройках.\n\n"<annotation id="link">"Настройки"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Чтобы временно скрыть кнопку, переместите ее к краю экрана"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перенести в левый верхний угол"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перенести в правый верхний угол"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Перенести в левый нижний угол"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Перенести в правый нижний угол"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Перенести к краю и скрыть"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Вернуть из-за края и показать"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"включить или отключить"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Управление устройствами"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Чтобы добавить виджеты управления, выберите приложение"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Без автоподключения к мобильному интернету"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Нет подключения к интернету"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нет других доступных сетей"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 01be742..bd272c7 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"දීප්තිමත් බව"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"වර්ණ අපවර්තනය"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"වර්ණ නිවැරදි කිරීම"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"පරිශීලක සැකසීම්"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"නිමයි"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"වසන්න"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"සම්බන්ධිත"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"ජංගම දත්ත ක්‍රියාවිරහිත කරන්නද?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"ඔබට <xliff:g id="CARRIER">%s</xliff:g> හරහා දත්ත හෝ අන්තර්ජාලයට පිවිසීමේ හැකියාවක් නැත. අන්තර්ජාලය Wi-Fi හරහා පමණක් ලබා ගත හැකිය."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ඔබගේ වාහකය"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"යෙදුමක් අවසර ඉල්ලීමක් කරන නිසා, සැකසීම්වලට ඔබගේ ප්‍රතිචාරය සත්‍යාපනය කළ නොහැකිය."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> හට කොටස් <xliff:g id="APP_2">%2$s</xliff:g>ක් පෙන්වීමට ඉඩ දෙන්නද?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- එයට <xliff:g id="APP">%1$s</xliff:g> වෙතින් තොරතුරු කියවිය හැකිය"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"විශාලන කවුළු සැකසීම්"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ප්‍රවේශ්‍යතා විශේෂාංග විවෘත කිරීමට තට්ටු කරන්න. සැකසීම් තුළ මෙම බොත්තම අභිරුචිකරණය හෝ ප්‍රතිස්ථාපනය කරන්න.\n\n"<annotation id="link">"සැකසීම් බලන්න"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"එය තාවකාලිකව සැඟවීමට බොත්තම දාරයට ගෙන යන්න"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ඉහළ වමට ගෙන යන්න"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ඉහළ දකුණට ගෙන යන්න"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"පහළ වමට ගෙන යන්න"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"පහළ දකුණට ගෙන යන්න"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"මායිමට ගෙන යන්න සහ සඟවන්න"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"මායිමෙන් පිටට ගන්න සහ පෙන්වන්න"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ටොගල් කරන්න"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"උපාංග පාලන"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"පාලන එක් කිරීමට යෙදුම තෝරා ගන්න"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"ජංගම දත්ත ස්වංක්‍රියව සම්බන්ධ නොවනු ඇත"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"සම්බන්ධතාවයක් නැත"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ලබා ගත හැකි වෙනත් ජාල නැත"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 76dce74..a0dd3c3 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzia farieb"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Úprava farieb"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Používateľské nastavenia"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zavrieť"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Pripojené"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Chcete vypnúť mobilné dáta?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nebudete mať prístup k dátam ani internetu prostredníctvom operátora <xliff:g id="CARRIER">%s</xliff:g>. Internet bude k dispozícii iba cez Wi‑Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"váš operátor"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Nastavenia nemôžu overiť vašu odpoveď, pretože určitá aplikácia blokuje žiadosť o povolenie."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Povoliť aplikácii <xliff:g id="APP_0">%1$s</xliff:g> zobrazovať rezy z aplikácie <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Môže čítať informácie z aplikácie <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Nastavenia okna lupy"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Funkcie dostupnosti otvoríte klepnutím. Tlačidlo prispôsobte alebo nahraďte v Nastav.\n\n"<annotation id="link">"Zobraz. nast."</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Ak chcete tlačidlo dočasne skryť, presuňte ho k okraju"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Presunúť doľava nahor"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Presunúť doprava nahor"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Presunúť doľava nadol"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Presunúť doprava nadol"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Presunúť k okraju a skryť"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Presunúť z okraja a zobraziť"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"prepínač"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Ovládanie zariadení"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Vyberte aplikáciu, ktorej ovládače si chcete pridať"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Automatické pripojenie cez mobilné dáta nefunguje"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bez pripojenia"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nie sú k dispozícii žiadne ďalšie siete"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index cb306c2..44108c3 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Svetlost"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija barv"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Popravljanje barv"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Uporabniške nastavitve"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Končano"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zapri"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Povezava je vzpostavljena"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Želite izklopiti prenos podatkov v mobilnih omrežjih?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prek operaterja »<xliff:g id="CARRIER">%s</xliff:g>« ne boste imeli dostopa do podatkovne povezave ali interneta. Internet bo na voljo samo prek povezave Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"svojega operaterja"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Ker aplikacija zakriva zahtevo za dovoljenje, z nastavitvami ni mogoče preveriti vašega odziva."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Želite dovoliti, da aplikacija <xliff:g id="APP_0">%1$s</xliff:g> prikaže izreze aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– lahko bere podatke v aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Nastavitve okna povečevalnika"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Dotaknite se za funkcije za ljudi s posebnimi potrebami. Ta gumb lahko prilagodite ali zamenjate v nastavitvah.\n\n"<annotation id="link">"Ogled nastavitev"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Če želite gumb začasno skriti, ga premaknite ob rob."</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premakni zgoraj levo"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premakni zgoraj desno"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Premakni spodaj levo"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Premakni spodaj desno"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Premakni na rob in skrij"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Premakni z roba in pokaži"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"preklop"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrolniki naprave"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Izberite aplikacijo za dodajanje kontrolnikov"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilna podatkovna povezava ne bo samodejna."</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ni povezave"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nobeno drugo omrežje ni na voljo"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 9a6d73c..7630261 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ndriçimi"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Anasjellja e ngjyrës"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korrigjimi i ngjyrës"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Cilësimet e përdoruesit"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"U krye"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Mbyll"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"I lidhur"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Të çaktivizohen të dhënat celulare?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nuk do të kesh qasje te të dhënat ose interneti nëpërmjet <xliff:g id="CARRIER">%s</xliff:g>. Interneti do të ofrohet vetëm nëpërmjet Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatori yt celular"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Duke qenë se një aplikacion po bllokon një kërkesë për leje, \"Cilësimet\" nuk mund të verifikojnë përgjigjen tënde."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Të lejohet <xliff:g id="APP_0">%1$s</xliff:g> që të shfaqë pjesë të <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Mund të lexojë informacion nga <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Cilësimet e dritares së zmadhimit"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Trokit dhe hap veçoritë e qasshmërisë. Modifiko ose ndërro butonin te \"Cilësimet\".\n\n"<annotation id="link">"Shih cilësimet"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Zhvendose butonin në skaj për ta fshehur përkohësisht"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Zhvendos lart majtas"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Zhvendos lart djathtas"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Zhvendos poshtë majtas"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Zhvendos poshtë djathtas"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Zhvendose te skaji dhe fshihe"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Zhvendose jashtë skajit dhe shfaqe"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aktivizo/çaktivizo"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrollet e pajisjes"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Zgjidh aplikacionin për të shtuar kontrollet"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Të dhënat celulare nuk do të lidhen automatikisht"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nuk ka lidhje"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nuk ofrohet asnjë rrjet tjetër"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 0dff11f..2a7b88f 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветљеност"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверзија боја"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекција боја"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Корисничка подешавања"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Затвори"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Повезан"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Желите да искључите мобилне податке?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Нећете имати приступ подацима или интернету преко мобилног оператера <xliff:g id="CARRIER">%s</xliff:g>. Интернет ће бити доступан само преко WiFi везе."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"мобилни оператер"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Подешавања не могу да верификују ваш одговор јер апликација скрива захтев за дозволу."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Желите ли да дозволите апликацији <xliff:g id="APP_0">%1$s</xliff:g> да приказује исечке из апликације <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Може да чита податке из апликације <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Подешавања прозора за увећање"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Додирните за функције приступачности. Прилагодите или замените ово дугме у Подешавањима.\n\n"<annotation id="link">"Подешавања"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Померите дугме до ивице да бисте га привремено сакрили"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Премести горе лево"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Премести доле лево"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Премести доле десно"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Премести до ивице и сакриј"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Премести изван ивице и прикажи"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"укључите/искључите"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Контроле уређаја"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Одаберите апликацију за додавање контрола"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Није успело аутом. повезивање преко моб. података"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Веза није успостављена"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Није доступна ниједна друга мрежа"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c1cdc5b..48654d5 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ljusstyrka"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Färginvertering"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Färgkorrigering"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Användarinställningar"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Klart"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Stäng"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Ansluten"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vill du inaktivera mobildata?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Du kan inte skicka data eller använda internet via <xliff:g id="CARRIER">%s</xliff:g>. Internetanslutning blir bara möjlig via wifi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"din operatör"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Svaret kan inte verifieras av Inställningar eftersom en app skymmer en begäran om behörighet."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Tillåter du att bitar av <xliff:g id="APP_2">%2$s</xliff:g> visas i <xliff:g id="APP_0">%1$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– Kan läsa information från <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Inställningar för förstoringsfönster"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Tryck för att öppna tillgänglighetsfunktioner. Anpassa/ersätt knappen i Inställningar.\n\n"<annotation id="link">"Inställningar"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flytta knappen till kanten för att dölja den tillfälligt"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flytta högst upp till vänster"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flytta högst upp till höger"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Flytta längst ned till vänster"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Flytta längst ned till höger"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Flytta till kanten och dölj"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Flytta från kanten och visa"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aktivera och inaktivera"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyrning"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Välj en app om du vill lägga till snabbkontroller"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Du ansluts inte till mobildata automatiskt"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ingen anslutning"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Inga andra nätverk är tillgängliga"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 8ed824e..6bf2bc2 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ung\'avu"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ugeuzaji rangi"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Usahihishaji wa rangirangi"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Mipangilio ya mtumiaji"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Nimemaliza"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Funga"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Imeunganishwa"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Ungependa kuzima data ya mtandao wa simu?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Hutaweza kufikia data au intaneti kupitia <xliff:g id="CARRIER">%s</xliff:g>. Intaneti itapatikana kupitia Wi-Fi pekee."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"mtoa huduma wako"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Kwa sababu programu nyingine inazuia ombi la ruhusa, hatuwezi kuthibitisha jibu lako katika Mipangilio."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Ungependa kuruhusu <xliff:g id="APP_0">%1$s</xliff:g> ionyeshe vipengee <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Inaweza kusoma maelezo kutoka <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Mipangilio ya dirisha la kikuzaji"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Gusa ili ufungue vipengele vya ufikivu. Weka mapendeleo au ubadilishe kitufe katika Mipangilio.\n\n"<annotation id="link">"Angalia mipangilio"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Sogeza kitufe kwenye ukingo ili ukifiche kwa muda"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sogeza juu kushoto"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sogeza juu kulia"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Sogeza chini kushoto"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Sogeza chini kulia"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Sogeza kwenye ukingo kisha ufiche"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Sogeza nje ya ukingo kisha uonyeshe"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"geuza"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Vidhibiti vya vifaa"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Chagua programu ili uweke vidhibiti"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Data ya mtandao wa simu haitaunganishwa kiotomatiki"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Hakuna muunganisho"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Hakuna mitandao mingine inayopatikana"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 9397b6c..b291004 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ஒளிர்வு"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"கலர் இன்வெர்ஷன்"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"கலர் கரெக்‌ஷன்"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"பயனர் அமைப்புகள்"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"முடிந்தது"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"மூடுக"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"இணைக்கப்பட்டது"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"மொபைல் டேட்டாவை ஆஃப் செய்யவா?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> மூலம் டேட்டா அல்லது இணையத்தை உங்களால் பயன்படுத்த முடியாது. வைஃபை வழியாக மட்டுமே இணையத்தைப் பயன்படுத்த முடியும்."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"உங்கள் மொபைல் நிறுவனம்"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"அனுமதிக் கோரிக்கையை ஆப்ஸ் மறைப்பதால், அமைப்புகளால் உங்கள் பதிலைச் சரிபார்க்க முடியாது."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> ஆப்ஸை, <xliff:g id="APP_2">%2$s</xliff:g> ஆப்ஸின் விழிப்பூட்டல்களைக் காண்பிக்க அனுமதிக்கவா?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- இது, <xliff:g id="APP">%1$s</xliff:g> பயன்பாட்டிலிருந்து தகவலைப் படிக்கும்"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"சாளரத்தைப் பெரிதாக்கும் கருவிக்கான அமைப்புகள்"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"அணுகல்தன்மை அம்சத்தை திறக்க தட்டவும். அமைப்பில் பட்டனை பிரத்தியேகமாக்கலாம்/மாற்றலாம்.\n\n"<annotation id="link">"அமைப்பில் காண்க"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"பட்டனைத் தற்காலிகமாக மறைக்க ஓரத்திற்கு நகர்த்தும்"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"மேலே இடதுபுறத்திற்கு நகர்த்து"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"மேலே வலதுபுறத்திற்கு நகர்த்து"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"கீழே இடதுபுறத்திற்கு நகர்த்து"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"கீழே வலதுபுறத்திற்கு நகர்த்து"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ஓரத்திற்கு நகர்த்தி மறை"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ஓரத்திற்கு நகர்த்தி, காட்டு"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"நிலைமாற்று"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"சாதனக் கட்டுப்பாடுகள்"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"கட்டுப்பாடுகளைச் சேர்க்க வேண்டிய ஆப்ஸைத் தேர்ந்தெடுங்கள்"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"மொபைல் டேட்டாவுடன் தானாக இணைக்காது"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"இணைப்பு இல்லை"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"வேறு நெட்வொர்க்குகள் எதுவும் கிடைக்கவில்லை"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index f202fae..4c34f45 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ప్రకాశం"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"కలర్ మార్పిడి"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"కలర్ కరెక్షన్"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"యూజర్ సెట్టింగ్‌లు"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"పూర్తయింది"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"మూసివేయి"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"కనెక్ట్ చేయబడినది"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"మొబైల్ డేటాను ఆఫ్ చేయాలా?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"\"<xliff:g id="CARRIER">%s</xliff:g>\" ద్వారా మీకు డేటా లేదా ఇంటర్నెట్‌కు యాక్సెస్ ఉండదు. Wi-Fi ద్వారా మాత్రమే ఇంటర్నెట్ అందుబాటులో ఉంటుంది."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"మీ క్యారియర్"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"అనుమతి రిక్వెస్ట్‌కు ఒక యాప్ అడ్డు తగులుతున్నందున సెట్టింగ్‌లు మీ ప్రతిస్పందనను ధృవీకరించలేకపోయాయి."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> స్లైస్‌లను చూపించడానికి <xliff:g id="APP_0">%1$s</xliff:g>ని అనుమతించండి?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ఇది <xliff:g id="APP">%1$s</xliff:g> నుండి సమాచారాన్ని చదువుతుంది"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"మాగ్నిఫయర్ విండో సెట్టింగ్‌లు"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"యాక్సెసిబిలిటీ ఫీచర్‌లను తెరవడానికి ట్యాప్ చేయండి. సెట్టింగ్‌లలో ఈ బటన్‌ను అనుకూలంగా మార్చండి లేదా రీప్లేస్ చేయండి.\n\n"<annotation id="link">"వీక్షణ సెట్టింగ్‌లు"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"తాత్కాలికంగా దానిని దాచడానికి బటన్‌ను చివరకు తరలించండి"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ఎగువ ఎడమ వైపునకు తరలించు"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ఎగువ కుడి వైపునకు తరలించు"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"దిగువ ఎడమ వైపునకు తరలించు"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"దిగువ కుడి వైపునకు తరలించు"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"అంచుకు తరలించి దాచండి"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"అంచుని తరలించి చూపించు"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"టోగుల్ చేయి"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"డివైజ్ కంట్రోల్స్"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"కంట్రోల్స్‌ను యాడ్ చేయడానికి యాప్‌ను ఎంచుకోండి"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"మొబైల్ డేటా ఆటోమెటిక్‌గా కనెక్ట్ అవ్వదు"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"కనెక్షన్ లేదు"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ఇతర నెట్‌వర్క్‌లేవీ అందుబాటులో లేవు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index f216437..5de908c 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ความสว่าง"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"การกลับสี"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"การแก้สี"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"การตั้งค่าผู้ใช้"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"จัดการผู้ใช้"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"เสร็จสิ้น"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ปิด"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"เชื่อมต่อ"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"ปิดอินเทอร์เน็ตมือถือไหม"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"คุณจะใช้เน็ตมือถือหรืออินเทอร์เน็ตผ่าน \"<xliff:g id="CARRIER">%s</xliff:g>\" ไม่ได้ แต่จะใช้ผ่าน Wi-Fi ได้เท่านั้น"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ผู้ให้บริการของคุณ"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"เนื่องจากแอปหนึ่งได้บดบังคำขอสิทธิ์ ระบบจึงไม่สามารถยืนยันคำตอบของคุณสำหรับการตั้งค่าได้"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"อนุญาตให้ <xliff:g id="APP_0">%1$s</xliff:g> แสดงส่วนต่างๆ ของ <xliff:g id="APP_2">%2$s</xliff:g>"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- อ่านข้อมูลจาก <xliff:g id="APP">%1$s</xliff:g> ได้"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"การตั้งค่าหน้าต่างแว่นขยาย"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"แตะเพื่อเปิดฟีเจอร์การช่วยเหลือพิเศษ ปรับแต่งหรือแทนที่ปุ่มนี้ในการตั้งค่า\n\n"<annotation id="link">"ดูการตั้งค่า"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ย้ายปุ่มไปที่ขอบเพื่อซ่อนชั่วคราว"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ย้ายไปด้านซ้ายบน"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ย้ายไปด้านขวาบน"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"ย้ายไปด้านซ้ายล่าง"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"ย้ายไปด้านขาวล่าง"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"ย้ายไปที่ขอบและซ่อน"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"ย้ายออกจากขอบและแสดง"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"สลับ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ระบบควบคุมอุปกรณ์"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"เลือกแอปเพื่อเพิ่มตัวควบคุม"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"อินเทอร์เน็ตมือถือจะไม่เชื่อมต่ออัตโนมัติ"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"ไม่มีการเชื่อมต่อ"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ไม่มีเครือข่ายอื่นๆ ที่พร้อมใช้งาน"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index f1acf43..f47b906 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -248,7 +248,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Pag-invert ng kulay"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Pagtatama ng kulay"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Mga setting ng user"</string>
+    <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Pamahalaan ang mga user"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Tapos na"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Isara"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Nakakonekta"</string>
@@ -727,6 +727,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"I-off ang mobile data?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Hindi ka magkaka-access sa data o internet sa pamamagitan ng <xliff:g id="CARRIER">%s</xliff:g>. Available lang ang internet sa pamamagitan ng Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ang iyong carrier"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Hindi ma-verify ng Mga Setting ang iyong tugon dahil may app na tumatakip sa isang kahilingan sa pagpapahintulot."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Payagan ang <xliff:g id="APP_0">%1$s</xliff:g> na ipakita ang mga slice ng <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Nakakabasa ito ng impormasyon mula sa <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +793,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Mga setting ng window ng magnifier"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"I-tap, buksan mga feature ng accessibility. I-customize o palitan button sa Mga Setting.\n\n"<annotation id="link">"Tingnan ang mga setting"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Ilipat ang button sa gilid para pansamantala itong itago"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Ilipat sa kaliwa sa itaas"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Ilipat sa kanan sa itaas"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Ilipat sa kaliwa sa ibaba"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Ilipat sa kanan sa ibaba"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Ilipat sa sulok at itago"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Alisin sa sulok at ipakita"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"i-toggle"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Mga kontrol ng device"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Pumili ng app para magdagdag ng mga kontrol"</string>
@@ -933,6 +947,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Hindi awtomatikong kokonekta ang mobile data"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Walang koneksyon"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Walang available na iba pang network"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 7b52a41..17dfa48 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaklık"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rengi ters çevirme"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Renk düzeltme"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Kullanıcı ayarları"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Bitti"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Kapat"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Bağlı"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Mobil veri kapatılsın mı?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> üzerinden veri veya internet erişiminiz olmayacak. İnternet yalnızca kablosuz bağlantı üzerinden kullanılabilecek."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatörünüz"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Bir uygulama bir izin isteğinin anlaşılmasını engellediğinden, Ayarlar, yanıtınızı doğrulayamıyor."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> uygulamasının, <xliff:g id="APP_2">%2$s</xliff:g> dilimlerini göstermesine izin verilsin mi?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g> uygulamasından bilgileri okuyabilir"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Büyüteç penceresi ayarları"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Erişilebilirlik özelliklerini açmak için dokunun. Bu düğmeyi Ayarlar\'dan özelleştirin veya değiştirin.\n\n"<annotation id="link">"Ayarları göster"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düğmeyi geçici olarak gizlemek için kenara taşıyın"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sol üste taşı"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sağ üste taşı"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Sol alta taşı"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Sağ alta taşı"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Kenara taşıyıp gizle"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Kenarın dışına taşıyıp göster"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"değiştir"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Cihaz denetimleri"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Denetim eklemek için uygulama seçin"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobil veri otomatik olarak bağlanmıyor"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bağlantı yok"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Kullanılabilir başka ağ yok"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 6bd9e30..ff1b594 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яскравість"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Інверсія кольорів"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекція кольору"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Налаштування користувача"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Закрити"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Під’єднано"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Вимкнути мобільний Інтернет?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Ви не матимете доступу до даних чи Інтернету через оператора <xliff:g id="CARRIER">%s</xliff:g>. Інтернет буде доступний лише через Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ваш оператор"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Не вдається підтвердити вашу відповідь у налаштуваннях, оскільки інший додаток заступає запит на дозвіл."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Дозволити додатку <xliff:g id="APP_0">%1$s</xliff:g> показувати фрагменти додатка <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Має доступ до інформації з додатка <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Налаштування розміру лупи"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Кнопка спеціальних можливостей. Змініть або замініть її в Налаштуваннях.\n\n"<annotation id="link">"Переглянути налаштування"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Щоб тимчасово сховати кнопку, перемістіть її на край екрана"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перемістити ліворуч угору"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перемістити праворуч угору"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Перемістити ліворуч униз"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Перемістити праворуч униз"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Перемістити до краю, приховати"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Перемістити від краю, показати"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"перемкнути"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Керування пристроями"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Виберіть, для якого додатка налаштувати елементи керування"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Мобільний Інтернет не підключатиметься автоматично"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Немає з\'єднання"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Інші мережі недоступні"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index ec382b2..10e0e68 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"چمکیلا پن"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"رنگوں کی تقلیب"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"رنگ کی اصلاح"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"صارف کی ترتیبات"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"ہو گیا"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"بند کریں"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"مربوط"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"موبائل ڈیٹا آف کریں؟"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"‏آپ کو <xliff:g id="CARRIER">%s</xliff:g> کے ذریعے ڈیٹا یا انٹرنیٹ تک رسائی حاصل نہیں ہوگی۔ انٹرنیٹ صرف Wi-Fi کے ذریعے دستیاب ہوگا۔"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"آپ کا کریئر"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"چونکہ ایک ایپ اجازت کی درخواست کو مبہم کر رہی ہے، لہذا ترتیبات آپ کے جواب کی توثیق نہیں کر سکتی ہیں۔"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> کو <xliff:g id="APP_2">%2$s</xliff:g> کے سلائسز دکھانے کی اجازت دیں؟"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- یہ <xliff:g id="APP">%1$s</xliff:g> کی معلومات پڑھ سکتا ہے"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"میگنیفائر ونڈو کی ترتیبات"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"ایکسیسبیلٹی خصوصیات کھولنے کے لیے تھپتھپائیں۔ ترتیبات میں اس بٹن کو حسب ضرورت بنائیں یا تبدیل کریں۔\n\n"<annotation id="link">"ترتیبات ملاحظہ کریں"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"عارضی طور پر بٹن کو چھپانے کے لئے اسے کنارے پر لے جائیں"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"اوپر بائیں جانب لے جائیں"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"اوپر دائیں جانب لے جائيں"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"نیچے بائیں جانب لے جائیں"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"نیچے دائیں جانب لے جائیں"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"‏EDGE پر لے جائیں اور چھپائیں"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"‏EDGE اور شو سے باہر منتقل کریں"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ٹوگل کریں"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"آلہ کے کنٹرولز"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"کنٹرولز شامل کرنے کے لیے ایپ منتخب کریں"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"موبائل ڈیٹا خودکار طور پر منسلک نہیں ہوگا"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"کوئی کنکشن نہیں"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"کوئی دوسرا نیٹ ورک دستیاب نہیں ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index a666432..184044d 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Yorqinlik"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ranglarni akslantirish"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ranglarni tuzatish"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Foydalanuvchi sozlamalari"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Tayyor"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Yopish"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Ulangan"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Mobil internet uzilsinmi?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> orqali internetdan foydalana olmaysiz. Internet faqat Wi-Fi orqali ishlaydi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"aloqa operatoringiz"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Ilova ruxsatnoma so‘roviga xalaqit qilayotgani tufayli, “Sozlamalar” ilovasi javobingizni tekshira olmaydi."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> ilovasiga <xliff:g id="APP_2">%2$s</xliff:g> ilovasidan fragmentlar ko‘rsatishga ruxsat berilsinmi?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"– <xliff:g id="APP">%1$s</xliff:g> ma’lumotlarini o‘qiy oladi"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Lupa oynasi sozlamalari"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Maxsus imkoniyatlarni ochish uchun bosing Sozlamalardan moslay yoki almashtira olasiz.\n\n"<annotation id="link">"Sozlamalar"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Vaqtinchalik berkitish uchun tugmani qirra tomon suring"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuqori chapga surish"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Yuqori oʻngga surish"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Quyi chapga surish"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Quyi oʻngga surish"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Chetiga olib borish va yashirish"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Chetidan qaytarish va koʻrsatish"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"oʻzgartirish"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Qurilmalarni boshqarish"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Boshqaruv elementlarini kiritish uchun ilovani tanlang"</string>
@@ -933,6 +948,10 @@
     <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">"Ulandi"</string>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobil internetga avtomatik ulanmaydi"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Internetga ulanmagansiz"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Boshqa tarmoqlar mavjud emas"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 21200ca..8ea3408 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Độ sáng"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Đảo màu"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Chỉnh màu"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Cài đặt người dùng"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Xong"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Đóng"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Đã kết nối"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Tắt dữ liệu di động?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua chế độ <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"nhà mạng của bạn"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Vì ứng dụng đang che khuất yêu cầu cấp quyền nên Cài đặt không thể xác minh câu trả lời của bạn."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Cho phép <xliff:g id="APP_0">%1$s</xliff:g> hiển thị các lát của <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Có thể đọc thông tin từ <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Chế độ cài đặt cửa sổ phóng to"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Nhấn để mở bộ tính năng hỗ trợ tiếp cận. Tuỳ chỉnh/thay thế nút này trong phần Cài đặt.\n\n"<annotation id="link">"Xem chế độ cài đặt"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Di chuyển nút sang cạnh để ẩn nút tạm thời"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Chuyển lên trên cùng bên trái"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Chuyển lên trên cùng bên phải"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Chuyển tới dưới cùng bên trái"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Chuyển tới dưới cùng bên phải"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Chuyển đến cạnh và ẩn"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Chuyển ra xa cạnh và hiển thị"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"bật/tắt"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Điều khiển thiết bị"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Chọn ứng dụng để thêm các tùy chọn điều khiển"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Dữ liệu di động sẽ không tự động kết nối"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Không có kết nối mạng"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Không có mạng nào khác"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 83508a1..1cead42 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"颜色反转"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"用户设置"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"关闭"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"已连接"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"要关闭移动数据网络吗?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"您将无法通过<xliff:g id="CARRIER">%s</xliff:g>使用移动数据或互联网,只能通过 WLAN 连接到互联网。"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"您的运营商"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"由于某个应用遮挡了权限请求界面,因此“设置”应用无法验证您的回应。"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"要允许“<xliff:g id="APP_0">%1$s</xliff:g>”显示“<xliff:g id="APP_2">%2$s</xliff:g>”图块吗?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- 可以读取“<xliff:g id="APP">%1$s</xliff:g>”中的信息"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"放大镜窗口设置"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"点按即可打开无障碍功能。您可在“设置”中自定义或更换此按钮。\n\n"<annotation id="link">"查看设置"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"将按钮移到边缘,即可暂时将其隐藏"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移至左上角"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移至右上角"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"移至左下角"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"移至右下角"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"移至边缘并隐藏"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"移至边缘以外并显示"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"开启/关闭"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"设备控制器"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"选择要添加控制器的应用"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"系统将不会自动连接到移动数据网络"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"无网络连接"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"没有其他可用网络"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 28420b3..cb90614 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"色彩反轉"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"使用者設定"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"關閉"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"已連線"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"要關閉流動數據嗎?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"您無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用流動數據或互聯網。如要使用互聯網,您必須連接 Wi-Fi。"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"您的流動網絡供應商"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"由於某個應用程式已阻擋權限要求畫面,因此「設定」應用程式無法驗證您的回應。"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"要允許「<xliff:g id="APP_0">%1$s</xliff:g>」顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的快訊嗎?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- 可以讀取「<xliff:g id="APP">%1$s</xliff:g>」中的資料"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"放大鏡視窗設定"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"㩒一下就可以開無障礙功能。喺「設定」度自訂或者取代呢個按鈕。\n\n"<annotation id="link">"查看設定"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣即可暫時隱藏"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移去左上方"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移去右上方"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"移到左下方"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"移去右下方"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"移到邊緣並隱藏"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"從邊緣移出並顯示"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"切換"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"選擇要新增控制項的應用程式"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"不會自動連線至流動數據"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"沒有連線"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"沒有可用的其他網絡"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 8a1a9e2..ccad340 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"色彩反轉"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"使用者設定"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"關閉"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"已連線"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"要關閉行動數據嗎?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路。你只能透過 Wi-Fi 使用網際網路。"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"你的電信業者"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"由於某個應用程式覆蓋了權限要求畫面,因此「設定」應用程式無法驗證你的回應。"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"要允許「<xliff:g id="APP_0">%1$s</xliff:g>」顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的區塊嗎?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- 它可以讀取「<xliff:g id="APP">%1$s</xliff:g>」的資訊"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"放大鏡視窗設定"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"輕觸即可開啟無障礙功能。你可以前往「設定」自訂或更換這個按鈕。\n\n"<annotation id="link">"查看設定"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣處即可暫時隱藏"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移到左上方"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移到右上方"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"移到左下方"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"移到右下方"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"移到邊緣並隱藏"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"從邊緣移出並顯示"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"切換"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"選擇應用程式以新增控制項"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"系統將不會自動使用行動數據連線"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"沒有網路連線"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"沒有可用的其他網路"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 9b376f0..b346494 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -248,7 +248,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ukugqama"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ukuguqulwa kombala"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ukulungiswa kombala"</string>
-    <string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Amasethingi womsebenzisi"</string>
+    <!-- no translation found for quick_settings_more_user_settings (7634653308485206306) -->
+    <skip />
     <string name="quick_settings_done" msgid="2163641301648855793">"Kwenziwe"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Vala"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Ixhunyiwe"</string>
@@ -727,6 +728,14 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vala idatha yeselula?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Ngeke ube nokufinyelela kudatha noma ku-inthanethi nge-<xliff:g id="CARRIER">%s</xliff:g>. I-inthanethi izotholakala kuphela nge-Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"inkampani yakho yenethiwekhi"</string>
+    <!-- no translation found for auto_data_switch_disable_title (5146527155665190652) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_disable_message (5885533647399535852) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_negative_button (2370876875999891444) -->
+    <skip />
+    <!-- no translation found for auto_data_switch_dialog_positive_button (8531782041263087564) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Ngoba uhlelo lokusebenza lusitha isicelo semvume, Izilungiselelo azikwazi ukuqinisekisa impendulo yakho."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Vumela i-<xliff:g id="APP_0">%1$s</xliff:g> ukuthi ibonise izingcezu ze-<xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Ingafunda ulwazi kusukela ku-<xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -785,12 +794,18 @@
     <string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Amasethingi ewindi lesikhulisi"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Thepha ukuze uvule izakhi zokufinyelela. Enza ngendlela oyifisayo noma shintsha le nkinobho Kumasethingi.\n\n"<annotation id="link">"Buka amasethingi"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Hambisa inkinobho onqenqemeni ukuze uyifihle okwesikhashana"</string>
+    <!-- no translation found for accessibility_floating_button_undo (511112888715708241) -->
+    <skip />
+    <!-- no translation found for accessibility_floating_button_undo_message_text (3044079592757099698) -->
+    <skip />
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Hamba phezulu kwesokunxele"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Hamba phezulu ngakwesokudla"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Hamba phansi ngakwesokunxele"</string>
     <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Hamba phansi ngakwesokudla"</string>
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Hamba onqenqemeni ufihle"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Phuma onqenqemeni ubonise"</string>
+    <!-- no translation found for accessibility_floating_button_action_remove_menu (6730432848162552135) -->
+    <skip />
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"guqula"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Izilawuli zezinsiza"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Khetha uhlelo lokusebenza ukwengeza izilawuli"</string>
@@ -933,6 +948,10 @@
     <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>
+    <!-- no translation found for mobile_data_temp_connection_active (4590222725908806824) -->
+    <skip />
+    <!-- no translation found for mobile_data_poor_connection (819617772268371434) -->
+    <skip />
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Idatha yeselula ngeke ikwazi ukuxhuma ngokuzenzekelayo"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Alukho uxhumano"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Awekho amanye amanethiwekhi atholakalayo"</string>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 7ca42f7..4fd25a9 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -177,6 +177,7 @@
     <item type="id" name="action_move_bottom_right"/>
     <item type="id" name="action_move_to_edge_and_hide"/>
     <item type="id" name="action_move_out_edge_and_show"/>
+    <item type="id" name="action_remove_menu"/>
 
     <!-- rounded corner view id -->
     <item type="id" name="rounded_corner_top_left"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d5d99cc..b325c56 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2218,6 +2218,8 @@
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half">Move to edge and hide</string>
     <!-- Action in accessibility menu to move the accessibility floating button out the edge and show. [CHAR LIMIT=36]-->
     <string name="accessibility_floating_button_action_move_out_edge_and_show">Move out edge and show</string>
+    <!-- Action in accessibility menu to remove the accessibility floating menu view on the screen. [CHAR LIMIT=36]-->
+    <string name="accessibility_floating_button_action_remove_menu">Remove</string>
     <!-- Action in accessibility menu to toggle on/off the accessibility feature. [CHAR LIMIT=30]-->
     <string name="accessibility_floating_button_action_double_tap_to_toggle">toggle</string>
 
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index 9115d42..148e5ec 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -34,6 +34,16 @@
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent" />
 
+    <!-- Touch ripple must have the same constraint as the album art. -->
+    <Constraint
+        android:id="@+id/touch_ripple_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_media_session_height_collapsed"
+        app:layout_constraintStart_toStartOf="@+id/album_art"
+        app:layout_constraintEnd_toEndOf="@+id/album_art"
+        app:layout_constraintTop_toTopOf="@+id/album_art"
+        app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+
     <Constraint
         android:id="@+id/header_title"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml
index 522dc68..ac484d7 100644
--- a/packages/SystemUI/res/xml/media_session_expanded.xml
+++ b/packages/SystemUI/res/xml/media_session_expanded.xml
@@ -27,6 +27,16 @@
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent" />
 
+    <!-- Touch ripple must have the same constraint as the album art. -->
+    <Constraint
+        android:id="@+id/touch_ripple_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_media_session_height_expanded"
+        app:layout_constraintStart_toStartOf="@+id/album_art"
+        app:layout_constraintEnd_toEndOf="@+id/album_art"
+        app:layout_constraintTop_toTopOf="@+id/album_art"
+        app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+
     <Constraint
         android:id="@+id/header_title"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index da1d233..3961438 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -237,6 +237,9 @@
     ) {
         var isActive: Boolean = fraction < 0.5f
         fun update(newFraction: Float): Pair<Boolean, Boolean> {
+            if (newFraction == fraction) {
+                return Pair(isActive, false)
+            }
             val wasActive = isActive
             val hasJumped =
                 (fraction == 0f && newFraction == 1f) || (fraction == 1f && newFraction == 0f)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 7e42e1b..8ac1de8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -85,13 +85,12 @@
         mTmpSourceRectF.set(sourceBounds);
         mTmpDestinationRect.set(sourceBounds);
         mTmpDestinationRect.inset(insets);
-        // Scale by the shortest edge and offset such that the top/left of the scaled inset
-        // source rect aligns with the top/left of the destination bounds
+        // Scale to the bounds no smaller than the destination and offset such that the top/left
+        // of the scaled inset source rect aligns with the top/left of the destination bounds
         final float scale;
         if (sourceRectHint.isEmpty() || sourceRectHint.width() == sourceBounds.width()) {
-            scale = sourceBounds.width() <= sourceBounds.height()
-                    ? (float) destinationBounds.width() / sourceBounds.width()
-                    : (float) destinationBounds.height() / sourceBounds.height();
+            scale = Math.max((float) destinationBounds.width() / sourceBounds.width(),
+                    (float) destinationBounds.height() / sourceBounds.height());
         } else {
             // scale by sourceRectHint if it's not edge-to-edge
             final float endScale = sourceRectHint.width() <= sourceRectHint.height()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 4613e8b..e743ec8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -110,4 +110,9 @@
       * Sent when screen started turning off.
       */
      void onScreenTurningOff() = 24;
+
+     /**
+      * Sent when split keyboard shortcut is triggered to enter stage split.
+      */
+     void enterStageSplitFromRunningApp(boolean leftOrTop) = 25;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
similarity index 74%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt
rename to packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
index cd4b999..0ee813b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
@@ -24,15 +24,13 @@
 import java.io.PrintWriter
 import java.util.concurrent.Executor
 
-/**
- * Class for instance of RegionSamplingHelper
- */
-open class RegionSamplingInstance(
-        sampledView: View?,
-        mainExecutor: Executor?,
-        bgExecutor: Executor?,
-        regionSamplingEnabled: Boolean,
-        updateFun: UpdateColorCallback
+/** Class for instance of RegionSamplingHelper */
+open class RegionSampler(
+    sampledView: View?,
+    mainExecutor: Executor?,
+    bgExecutor: Executor?,
+    regionSamplingEnabled: Boolean,
+    updateFun: UpdateColorCallback
 ) {
     private var regionDarkness = RegionDarkness.DEFAULT
     private var samplingBounds = Rect()
@@ -40,23 +38,13 @@
     @VisibleForTesting var regionSampler: RegionSamplingHelper? = null
     private var lightForegroundColor = Color.WHITE
     private var darkForegroundColor = Color.BLACK
-    /**
-     * Interface for method to be passed into RegionSamplingHelper
-     */
-    @FunctionalInterface
-    interface UpdateColorCallback {
-        /**
-         * Method to update the foreground colors after clock darkness changed.
-         */
-        fun updateColors()
-    }
 
     @VisibleForTesting
     open fun createRegionSamplingHelper(
-            sampledView: View,
-            callback: SamplingCallback,
-            mainExecutor: Executor?,
-            bgExecutor: Executor?
+        sampledView: View,
+        callback: SamplingCallback,
+        mainExecutor: Executor?,
+        bgExecutor: Executor?
     ): RegionSamplingHelper {
         return RegionSamplingHelper(sampledView, callback, mainExecutor, bgExecutor)
     }
@@ -77,7 +65,7 @@
      *
      * @return the determined foreground color
      */
-    fun currentForegroundColor(): Int{
+    fun currentForegroundColor(): Int {
         return if (regionDarkness.isDark) {
             lightForegroundColor
         } else {
@@ -97,41 +85,37 @@
         return regionDarkness
     }
 
-    /**
-     * Start region sampler
-     */
+    /** Start region sampler */
     fun startRegionSampler() {
         regionSampler?.start(samplingBounds)
     }
 
-    /**
-     * Stop region sampler
-     */
+    /** Stop region sampler */
     fun stopRegionSampler() {
         regionSampler?.stop()
     }
 
-    /**
-     * Dump region sampler
-     */
+    /** Dump region sampler */
     fun dump(pw: PrintWriter) {
         regionSampler?.dump(pw)
     }
 
     init {
         if (regionSamplingEnabled && sampledView != null) {
-            regionSampler = createRegionSamplingHelper(sampledView,
+            regionSampler =
+                createRegionSamplingHelper(
+                    sampledView,
                     object : SamplingCallback {
                         override fun onRegionDarknessChanged(isRegionDark: Boolean) {
                             regionDarkness = convertToClockDarkness(isRegionDark)
-                            updateFun.updateColors()
+                            updateFun()
                         }
                         /**
-                        * The method getLocationOnScreen is used to obtain the view coordinates
-                        * relative to its left and top edges on the device screen.
-                        * Directly accessing the X and Y coordinates of the view returns the
-                        * location relative to its parent view instead.
-                        */
+                         * The method getLocationOnScreen is used to obtain the view coordinates
+                         * relative to its left and top edges on the device screen. Directly
+                         * accessing the X and Y coordinates of the view returns the location
+                         * relative to its parent view instead.
+                         */
                         override fun getSampledRegion(sampledView: View): Rect {
                             val screenLocation = tmpScreenLocation
                             sampledView.getLocationOnScreen(screenLocation)
@@ -147,8 +131,13 @@
                         override fun isSamplingEnabled(): Boolean {
                             return regionSamplingEnabled
                         }
-                    }, mainExecutor, bgExecutor)
+                    },
+                    mainExecutor,
+                    bgExecutor
+                )
         }
         regionSampler?.setWindowVisible(true)
     }
 }
+
+typealias UpdateColorCallback = () -> Unit
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
index bb3df8f..7b216017 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -18,17 +18,15 @@
 
 import android.content.Context
 import android.os.Handler
-import com.android.internal.statusbar.IStatusBarService
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlagsDebug.ALL_FLAGS
 import com.android.systemui.util.settings.SettingsUtilModule
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
-import javax.inject.Named
 
 @Module(includes = [
     FeatureFlagsDebugStartableModule::class,
+    FlagsCommonModule::class,
     ServerFlagReaderModule::class,
     SettingsUtilModule::class,
 ])
@@ -43,20 +41,5 @@
         fun provideFlagManager(context: Context, @Main handler: Handler): FlagManager {
             return FlagManager(context, handler)
         }
-
-        @JvmStatic
-        @Provides
-        @Named(ALL_FLAGS)
-        fun providesAllFlags(): Map<Int, Flag<*>> = Flags.collectFlags()
-
-        @JvmStatic
-        @Provides
-        fun providesRestarter(barService: IStatusBarService): Restarter {
-            return object: Restarter {
-                override fun restart() {
-                    barService.restart()
-                }
-            }
-        }
     }
 }
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
index 0f7e732..aef8876 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
@@ -16,29 +16,15 @@
 
 package com.android.systemui.flags
 
-import com.android.internal.statusbar.IStatusBarService
 import dagger.Binds
 import dagger.Module
-import dagger.Provides
 
 @Module(includes = [
     FeatureFlagsReleaseStartableModule::class,
+    FlagsCommonModule::class,
     ServerFlagReaderModule::class
 ])
 abstract class FlagsModule {
     @Binds
     abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags
-
-    @Module
-    companion object {
-        @JvmStatic
-        @Provides
-        fun providesRestarter(barService: IStatusBarService): Restarter {
-            return object: Restarter {
-                override fun restart() {
-                    barService.restart()
-                }
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 40a96b0..d48d7ff 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -34,25 +34,27 @@
 import com.android.systemui.flags.Flags.REGION_SAMPLING
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.log.dagger.KeyguardClockLog
 import com.android.systemui.plugins.ClockController
 import com.android.systemui.plugins.log.LogBuffer
-import com.android.systemui.shared.regionsampling.RegionSamplingInstance
+import com.android.systemui.shared.regionsampling.RegionSampler
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
 import com.android.systemui.statusbar.policy.ConfigurationController
-import java.io.PrintWriter
-import java.util.Locale
-import java.util.TimeZone
-import java.util.concurrent.Executor
-import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.launch
+import java.io.PrintWriter
+import java.util.Locale
+import java.util.TimeZone
+import java.util.concurrent.Executor
+import javax.inject.Inject
 
 /**
  * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
@@ -69,14 +71,17 @@
     private val context: Context,
     @Main private val mainExecutor: Executor,
     @Background private val bgExecutor: Executor,
-    @KeyguardClockLog private val logBuffer: LogBuffer,
+    @KeyguardClockLog private val logBuffer: LogBuffer?,
     private val featureFlags: FeatureFlags
 ) {
     var clock: ClockController? = null
         set(value) {
             field = value
             if (value != null) {
-                value.setLogBuffer(logBuffer)
+                if (logBuffer != null) {
+                    value.setLogBuffer(logBuffer)
+                }
+
                 value.initialize(resources, dozeAmount, 0f)
                 updateRegionSamplers(value)
             }
@@ -139,21 +144,17 @@
             bgExecutor: Executor?,
             regionSamplingEnabled: Boolean,
             updateColors: () -> Unit
-    ): RegionSamplingInstance {
-        return RegionSamplingInstance(
+    ): RegionSampler {
+        return RegionSampler(
             sampledView,
             mainExecutor,
             bgExecutor,
             regionSamplingEnabled,
-            object : RegionSamplingInstance.UpdateColorCallback {
-                override fun updateColors() {
-                    updateColors()
-                }
-            })
+            updateFun = { updateColors() } )
     }
 
-    var smallRegionSampler: RegionSamplingInstance? = null
-    var largeRegionSampler: RegionSamplingInstance? = null
+    var smallRegionSampler: RegionSampler? = null
+    var largeRegionSampler: RegionSampler? = null
 
     private var smallClockIsDark = true
     private var largeClockIsDark = true
@@ -161,6 +162,7 @@
     private val configListener = object : ConfigurationController.ConfigurationListener {
         override fun onThemeChanged() {
             clock?.events?.onColorPaletteChanged(resources)
+            updateColors()
         }
 
         override fun onDensityOrFontScaleChanged() {
@@ -186,8 +188,10 @@
     private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
         override fun onKeyguardVisibilityChanged(visible: Boolean) {
             isKeyguardVisible = visible
-            if (!isKeyguardVisible) {
-                clock?.animations?.doze(if (isDozing) 1f else 0f)
+            if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
+                if (!isKeyguardVisible) {
+                    clock?.animations?.doze(if (isDozing) 1f else 0f)
+                }
             }
         }
 
@@ -224,6 +228,7 @@
                 listenForDozing(this)
                 if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
                     listenForDozeAmountTransition(this)
+                    listenForGoneToAodTransition(this)
                 } else {
                     listenForDozeAmount(this)
                 }
@@ -276,6 +281,22 @@
         }
     }
 
+    /**
+     * When keyguard is displayed again after being gone, the clock must be reset to full
+     * dozing.
+     */
+    @VisibleForTesting
+    internal fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
+        return scope.launch {
+            keyguardTransitionInteractor.goneToAodTransition.filter {
+                it.transitionState == TransitionState.STARTED
+            }.collect {
+                dozeAmount = 1f
+                clock?.animations?.doze(dozeAmount)
+            }
+        }
+    }
+
     @VisibleForTesting
     internal fun listenForDozing(scope: CoroutineScope): Job {
         return scope.launch {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 81305f9..0b395a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -223,7 +223,7 @@
         @Override
         public void onSwipeUp() {
             if (!mUpdateMonitor.isFaceDetectionRunning()) {
-                boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(true,
+                boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(
                         FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER);
                 mKeyguardSecurityCallback.userActivity();
                 if (didFaceAuthRun) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index cc1f2fe..39dc609 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1615,7 +1615,7 @@
                 @Override
                 public void onUdfpsPointerDown(int sensorId) {
                     mLogger.logUdfpsPointerDown(sensorId);
-                    requestFaceAuth(true, FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
+                    requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
                 }
 
                 /**
@@ -2357,14 +2357,12 @@
     /**
      * Requests face authentication if we're on a state where it's allowed.
      * This will re-trigger auth in case it fails.
-     * @param userInitiatedRequest true if the user explicitly requested face auth
      * @param reason One of the reasons {@link FaceAuthApiRequestReason} on why this API is being
      * invoked.
      * @return current face auth detection state, true if it is running.
      */
-    public boolean requestFaceAuth(boolean userInitiatedRequest,
-            @FaceAuthApiRequestReason String reason) {
-        mLogger.logFaceAuthRequested(userInitiatedRequest, reason);
+    public boolean requestFaceAuth(@FaceAuthApiRequestReason String reason) {
+        mLogger.logFaceAuthRequested(reason);
         updateFaceListeningState(BIOMETRIC_ACTION_START, apiRequestReasonToUiEvent(reason));
         return isFaceDetectionRunning();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index 46f3d4e..32ce537 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.plugins.log.LogLevel
 import com.android.systemui.plugins.log.LogLevel.DEBUG
 import com.android.systemui.plugins.log.LogLevel.ERROR
+import com.android.systemui.plugins.log.LogLevel.INFO
 import com.android.systemui.plugins.log.LogLevel.VERBOSE
 import com.android.systemui.plugins.log.LogLevel.WARNING
 import com.android.systemui.plugins.log.MessageInitializer
@@ -50,6 +51,14 @@
         buffer.log(TAG, DEBUG, messageInitializer, messagePrinter)
     }
 
+    fun v(msg: String, arg: Any) {
+        buffer.log(TAG, VERBOSE, { str1 = arg.toString() }, { "$msg: $str1" })
+    }
+
+    fun i(msg: String, arg: Any) {
+        buffer.log(TAG, INFO, { str1 = arg.toString() }, { "$msg: $str1" })
+    }
+
     // TODO: remove after b/237743330 is fixed
     fun logStatusBarCalculatedAlpha(alpha: Float) {
         debugLog({ double1 = alpha.toDouble() }, { "Calculated new alpha: $double1" })
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 31fc320..3308f55 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -111,11 +111,10 @@
                 }, { "Face help received, msgId: $int1 msg: $str1" })
     }
 
-    fun logFaceAuthRequested(userInitiatedRequest: Boolean, reason: String?) {
+    fun logFaceAuthRequested(reason: String?) {
         logBuffer.log(TAG, DEBUG, {
-            bool1 = userInitiatedRequest
             str1 = reason
-        }, { "requestFaceAuth() userInitiated=$bool1 reason=$str1" })
+        }, { "requestFaceAuth() reason=$str1" })
     }
 
     fun logFaceAuthSuccess(userId: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index d9f44cd..47ee71e 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -43,6 +43,7 @@
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.NotificationChannels;
 
 import java.util.Comparator;
@@ -137,7 +138,7 @@
                     if (mServicesStarted) {
                         final int N = mServices.length;
                         for (int i = 0; i < N; i++) {
-                            mServices[i].onBootCompleted();
+                            notifyBootCompleted(mServices[i]);
                         }
                     }
                 }
@@ -256,7 +257,7 @@
 
         for (i = 0; i < mServices.length; i++) {
             if (mBootCompleteCache.isBootComplete()) {
-                mServices[i].onBootCompleted();
+                notifyBootCompleted(mServices[i]);
             }
 
             mDumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
@@ -267,7 +268,13 @@
         mServicesStarted = true;
     }
 
-    private void timeInitialization(String clsName, Runnable init, TimingsTraceLog log,
+    private static void notifyBootCompleted(CoreStartable coreStartable) {
+        Trace.beginSection(coreStartable.getClass().getSimpleName() + ".onBootCompleted()");
+        coreStartable.onBootCompleted();
+        Trace.endSection();
+    }
+
+    private static void timeInitialization(String clsName, Runnable init, TimingsTraceLog log,
             String metricsPrefix) {
         long ti = System.currentTimeMillis();
         log.traceBegin(metricsPrefix + " " + clsName);
@@ -281,11 +288,13 @@
         }
     }
 
-    private CoreStartable startAdditionalStartable(String clsName) {
+    private static CoreStartable startAdditionalStartable(String clsName) {
         CoreStartable startable;
         if (DEBUG) Log.d(TAG, "loading: " + clsName);
         try {
+            Trace.beginSection(clsName + ".newInstance()");
             startable = (CoreStartable) Class.forName(clsName).newInstance();
+            Trace.endSection();
         } catch (ClassNotFoundException
                 | IllegalAccessException
                 | InstantiationException ex) {
@@ -295,14 +304,19 @@
         return startStartable(startable);
     }
 
-    private CoreStartable startStartable(String clsName, Provider<CoreStartable> provider) {
+    private static CoreStartable startStartable(String clsName, Provider<CoreStartable> provider) {
         if (DEBUG) Log.d(TAG, "loading: " + clsName);
-        return startStartable(provider.get());
+        Trace.beginSection("Provider<" + clsName + ">.get()");
+        CoreStartable startable = provider.get();
+        Trace.endSection();
+        return startStartable(startable);
     }
 
-    private CoreStartable startStartable(CoreStartable startable) {
+    private static CoreStartable startStartable(CoreStartable startable) {
         if (DEBUG) Log.d(TAG, "running: " + startable);
+        Trace.beginSection(startable.getClass().getSimpleName() + ".start()");
         startable.start();
+        Trace.endSection();
 
         return startable;
     }
@@ -340,11 +354,18 @@
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         if (mServicesStarted) {
-            mSysUIComponent.getConfigurationController().onConfigurationChanged(newConfig);
+            ConfigurationController configController = mSysUIComponent.getConfigurationController();
+            Trace.beginSection(
+                    configController.getClass().getSimpleName() + ".onConfigurationChanged()");
+            configController.onConfigurationChanged(newConfig);
+            Trace.endSection();
             int len = mServices.length;
             for (int i = 0; i < len; i++) {
                 if (mServices[i] != null) {
+                    Trace.beginSection(
+                            mServices[i].getClass().getSimpleName() + ".onConfigurationChanged()");
                     mServices[i].onConfigurationChanged(newConfig);
+                    Trace.endSection();
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java
index f56a154..ee048e1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java
@@ -172,4 +172,8 @@
                 R.dimen.dismiss_circle_small);
         mSizePercent = mMinDismissSize / maxDismissSize;
     }
+
+    interface DismissCallback {
+        void onDismiss();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index eb25232..396f584 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -35,6 +35,8 @@
 import androidx.dynamicanimation.animation.SpringForce;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.HashMap;
 
 /**
@@ -64,6 +66,7 @@
     private final Handler mHandler;
     private boolean mIsMovedToEdge;
     private boolean mIsFadeEffectEnabled;
+    private DismissAnimationController.DismissCallback mDismissCallback;
 
     // Cache the animations state of {@link DynamicAnimation.TRANSLATION_X} and {@link
     // DynamicAnimation.TRANSLATION_Y} to be well controlled by the touch handler
@@ -102,6 +105,11 @@
         }
     }
 
+    void setDismissCallback(
+            DismissAnimationController.DismissCallback dismissCallback) {
+        mDismissCallback = dismissCallback;
+    }
+
     void moveToTopLeftPosition() {
         mIsMovedToEdge = false;
         final Rect draggableBounds = mMenuView.getMenuDraggableBounds();
@@ -132,6 +140,13 @@
         constrainPositionAndUpdate(position);
     }
 
+    void removeMenu() {
+        Preconditions.checkArgument(mDismissCallback != null,
+                "The dismiss callback should be initialized first.");
+
+        mDismissCallback.onDismiss();
+    }
+
     void flingMenuThenSpringToEdge(float x, float velocityX, float velocityY) {
         final boolean shouldMenuFlingLeft = isOnLeftSide()
                 ? velocityX < ESCAPE_VELOCITY
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
index e69a248..ac5736b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
@@ -84,6 +84,12 @@
                 new AccessibilityNodeInfoCompat.AccessibilityActionCompat(moveEdgeId,
                         res.getString(moveEdgeTextResId));
         info.addAction(moveToOrOutEdge);
+
+        final AccessibilityNodeInfoCompat.AccessibilityActionCompat removeMenu =
+                new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                        R.id.action_remove_menu,
+                        res.getString(R.string.accessibility_floating_button_action_remove_menu));
+        info.addAction(removeMenu);
     }
 
     @Override
@@ -126,6 +132,11 @@
             return true;
         }
 
+        if (action == R.id.action_remove_menu) {
+            mAnimationController.removeMenu();
+            return true;
+        }
+
         return super.performAccessibilityAction(host, action, args);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index 034e96a..4a9807f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -136,11 +136,7 @@
         final Rect draggableBounds = getWindowAvailableBounds();
 
         // Initializes start position for mapping the translation of the menu view.
-        final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
-        final WindowInsets windowInsets = windowMetrics.getWindowInsets();
-        final Insets displayCutoutInsets = windowInsets.getInsetsIgnoringVisibility(
-                WindowInsets.Type.displayCutout());
-        draggableBounds.offset(-displayCutoutInsets.left, -displayCutoutInsets.top);
+        draggableBounds.offsetTo(/* newLeft= */ 0, /* newTop= */ 0);
 
         draggableBounds.top += margin;
         draggableBounds.right -= getMenuWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 3e620a2..33e155d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -100,6 +100,7 @@
                 windowManager);
         mMenuView = new MenuView(context, menuViewModel, menuViewAppearance);
         mMenuAnimationController = mMenuView.getMenuAnimationController();
+        mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage);
 
         mDismissView = new DismissView(context);
         mDismissAnimationController = new DismissAnimationController(mDismissView, mMenuView);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index b1a64ed..c7be907 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -74,7 +74,8 @@
         params.receiveInsetsIgnoringZOrder = true;
         params.privateFlags |= PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
         params.windowAnimations = android.R.style.Animation_Translucent;
-        params.setFitInsetsTypes(WindowInsets.Type.navigationBars());
+        params.setFitInsetsTypes(
+                WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
         return params;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index b50bfd7..f74c721 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -26,6 +26,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AlertDialog;
 import android.content.Context;
 import android.graphics.PixelFormat;
 import android.hardware.biometrics.BiometricAuthenticator.Modality;
@@ -63,6 +64,9 @@
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.biometrics.AuthController.ScaleFactorProvider;
+import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
+import com.android.systemui.biometrics.ui.CredentialView;
+import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -74,11 +78,13 @@
 import java.util.List;
 import java.util.Set;
 
+import javax.inject.Provider;
+
 /**
  * Top level container/controller for the BiometricPrompt UI.
  */
 public class AuthContainerView extends LinearLayout
-        implements AuthDialog, WakefulnessLifecycle.Observer {
+        implements AuthDialog, WakefulnessLifecycle.Observer, CredentialView.Host {
 
     private static final String TAG = "AuthContainerView";
 
@@ -112,15 +118,18 @@
     private final IBinder mWindowToken = new Binder();
     private final WindowManager mWindowManager;
     private final Interpolator mLinearOutSlowIn;
-    private final CredentialCallback mCredentialCallback;
     private final LockPatternUtils mLockPatternUtils;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final InteractionJankMonitor mInteractionJankMonitor;
 
+    // TODO: these should be migrated out once ready
+    private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor;
+    private final Provider<CredentialViewModel> mCredentialViewModelProvider;
+
     @VisibleForTesting final BiometricCallback mBiometricCallback;
 
     @Nullable private AuthBiometricView mBiometricView;
-    @Nullable private AuthCredentialView mCredentialView;
+    @Nullable private View mCredentialView;
     private final AuthPanelController mPanelController;
     private final FrameLayout mFrameLayout;
     private final ImageView mBackgroundView;
@@ -229,11 +238,13 @@
                 @NonNull WakefulnessLifecycle wakefulnessLifecycle,
                 @NonNull UserManager userManager,
                 @NonNull LockPatternUtils lockPatternUtils,
-                @NonNull InteractionJankMonitor jankMonitor) {
+                @NonNull InteractionJankMonitor jankMonitor,
+                @NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor,
+                @NonNull Provider<CredentialViewModel> credentialViewModelProvider) {
             mConfig.mSensorIds = sensorIds;
             return new AuthContainerView(mConfig, fpProps, faceProps, wakefulnessLifecycle,
-                    userManager, lockPatternUtils, jankMonitor, new Handler(Looper.getMainLooper()),
-                    bgExecutor);
+                    userManager, lockPatternUtils, jankMonitor, biometricPromptInteractor,
+                    credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor);
         }
     }
 
@@ -271,14 +282,51 @@
         }
     }
 
-    final class CredentialCallback implements AuthCredentialView.Callback {
-        @Override
-        public void onCredentialMatched(byte[] attestation) {
-            mCredentialAttestation = attestation;
-            animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
+    @Override
+    public void onCredentialMatched(@NonNull byte[] attestation) {
+        mCredentialAttestation = attestation;
+        animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
+    }
+
+    @Override
+    public void onCredentialAborted() {
+        sendEarlyUserCanceled();
+        animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+    }
+
+    @Override
+    public void onCredentialAttemptsRemaining(int remaining, @NonNull String messageBody) {
+        // Only show dialog if <=1 attempts are left before wiping.
+        if (remaining == 1) {
+            showLastAttemptBeforeWipeDialog(messageBody);
+        } else if (remaining <= 0) {
+            showNowWipingDialog(messageBody);
         }
     }
 
+    private void showLastAttemptBeforeWipeDialog(@NonNull String messageBody) {
+        final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
+                .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title)
+                .setMessage(messageBody)
+                .setPositiveButton(android.R.string.ok, null)
+                .create();
+        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+        alertDialog.show();
+    }
+
+    private void showNowWipingDialog(@NonNull String messageBody) {
+        final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
+                .setMessage(messageBody)
+                .setPositiveButton(
+                        com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss,
+                        null /* OnClickListener */)
+                .setOnDismissListener(
+                        dialog -> animateAway(AuthDialogCallback.DISMISSED_ERROR))
+                .create();
+        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+        alertDialog.show();
+    }
+
     @VisibleForTesting
     AuthContainerView(Config config,
             @Nullable List<FingerprintSensorPropertiesInternal> fpProps,
@@ -287,6 +335,8 @@
             @NonNull UserManager userManager,
             @NonNull LockPatternUtils lockPatternUtils,
             @NonNull InteractionJankMonitor jankMonitor,
+            @NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor,
+            @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
             @NonNull Handler mainHandler,
             @NonNull @Background DelayableExecutor bgExecutor) {
         super(config.mContext);
@@ -302,7 +352,6 @@
                 .getDimension(R.dimen.biometric_dialog_animation_translation_offset);
         mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
         mBiometricCallback = new BiometricCallback();
-        mCredentialCallback = new CredentialCallback();
 
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         mFrameLayout = (FrameLayout) layoutInflater.inflate(
@@ -314,6 +363,8 @@
         mPanelController = new AuthPanelController(mContext, mPanelView);
         mBackgroundExecutor = bgExecutor;
         mInteractionJankMonitor = jankMonitor;
+        mBiometricPromptInteractor = biometricPromptInteractor;
+        mCredentialViewModelProvider = credentialViewModelProvider;
 
         // Inflate biometric view only if necessary.
         if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
@@ -404,12 +455,12 @@
 
         switch (credentialType) {
             case Utils.CREDENTIAL_PATTERN:
-                mCredentialView = (AuthCredentialView) factory.inflate(
+                mCredentialView = factory.inflate(
                         R.layout.auth_credential_pattern_view, null, false);
                 break;
             case Utils.CREDENTIAL_PIN:
             case Utils.CREDENTIAL_PASSWORD:
-                mCredentialView = (AuthCredentialView) factory.inflate(
+                mCredentialView = factory.inflate(
                         R.layout.auth_credential_password_view, null, false);
                 break;
             default:
@@ -422,16 +473,12 @@
         mBackgroundView.setOnClickListener(null);
         mBackgroundView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
 
-        mCredentialView.setContainerView(this);
-        mCredentialView.setUserId(mConfig.mUserId);
-        mCredentialView.setOperationId(mConfig.mOperationId);
-        mCredentialView.setEffectiveUserId(mEffectiveUserId);
-        mCredentialView.setCredentialType(credentialType);
-        mCredentialView.setCallback(mCredentialCallback);
-        mCredentialView.setPromptInfo(mConfig.mPromptInfo);
-        mCredentialView.setPanelController(mPanelController, animatePanel);
-        mCredentialView.setShouldAnimateContents(animateContents);
-        mCredentialView.setBackgroundExecutor(mBackgroundExecutor);
+        mBiometricPromptInteractor.get().useCredentialsForAuthentication(
+                mConfig.mPromptInfo, credentialType, mConfig.mUserId, mConfig.mOperationId);
+        final CredentialViewModel vm = mCredentialViewModelProvider.get();
+        vm.setAnimateContents(animateContents);
+        ((CredentialView) mCredentialView).init(vm, this, mPanelController, animatePanel);
+
         mFrameLayout.addView(mCredentialView);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 9c78df5..313ff4157 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -72,6 +72,8 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
+import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -122,6 +124,10 @@
     private final Provider<UdfpsController> mUdfpsControllerFactory;
     private final Provider<SidefpsController> mSidefpsControllerFactory;
 
+    // TODO: these should be migrated out once ready
+    @NonNull private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor;
+    @NonNull private final Provider<CredentialViewModel> mCredentialViewModelProvider;
+
     private final Display mDisplay;
     private float mScaleFactor = 1f;
     // sensor locations without any resolution scaling nor rotation adjustments:
@@ -693,6 +699,8 @@
             @NonNull LockPatternUtils lockPatternUtils,
             @NonNull UdfpsLogger udfpsLogger,
             @NonNull StatusBarStateController statusBarStateController,
+            @NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor,
+            @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
             @NonNull InteractionJankMonitor jankMonitor,
             @Main Handler handler,
             @Background DelayableExecutor bgExecutor,
@@ -717,6 +725,9 @@
         mFaceEnrolledForUser = new SparseBooleanArray();
         mVibratorHelper = vibrator;
 
+        mBiometricPromptInteractor = biometricPromptInteractor;
+        mCredentialViewModelProvider = credentialViewModelProvider;
+
         mOrientationListener = new BiometricDisplayListener(
                 context,
                 mDisplayManager,
@@ -1079,6 +1090,11 @@
         return mUdfpsEnrolledForUser.get(userId);
     }
 
+    /** If BiometricPrompt is currently being shown to the user. */
+    public boolean isShowing() {
+        return mCurrentDialog != null;
+    }
+
     private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
         mCurrentDialogArgs = args;
 
@@ -1210,7 +1226,8 @@
                 .setMultiSensorConfig(multiSensorConfig)
                 .setScaleFactorProvider(() -> getScaleFactor())
                 .build(bgExecutor, sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle,
-                        userManager, lockPatternUtils, mInteractionJankMonitor);
+                        userManager, lockPatternUtils, mInteractionJankMonitor,
+                        mBiometricPromptInteractor, mCredentialViewModelProvider);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
deleted file mode 100644
index 76cd3f4..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.WindowInsets.Type.ime;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.graphics.Insets;
-import android.os.UserHandle;
-import android.text.InputType;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnApplyWindowInsetsListener;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.ImeAwareEditText;
-import android.widget.TextView;
-
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockscreenCredential;
-import com.android.internal.widget.VerifyCredentialResponse;
-import com.android.systemui.Dumpable;
-import com.android.systemui.R;
-
-import java.io.PrintWriter;
-
-/**
- * Pin and Password UI
- */
-public class AuthCredentialPasswordView extends AuthCredentialView
-        implements TextView.OnEditorActionListener, OnApplyWindowInsetsListener, Dumpable {
-
-    private static final String TAG = "BiometricPrompt/AuthCredentialPasswordView";
-
-    private final InputMethodManager mImm;
-    private ImeAwareEditText mPasswordField;
-    private ViewGroup mAuthCredentialHeader;
-    private ViewGroup mAuthCredentialInput;
-    private int mBottomInset = 0;
-
-    public AuthCredentialPasswordView(Context context,
-            AttributeSet attrs) {
-        super(context, attrs);
-        mImm = mContext.getSystemService(InputMethodManager.class);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mAuthCredentialHeader = findViewById(R.id.auth_credential_header);
-        mAuthCredentialInput = findViewById(R.id.auth_credential_input);
-        mPasswordField = findViewById(R.id.lockPassword);
-        mPasswordField.setOnEditorActionListener(this);
-        // TODO: De-dupe the logic with AuthContainerView
-        mPasswordField.setOnKeyListener((v, keyCode, event) -> {
-            if (keyCode != KeyEvent.KEYCODE_BACK) {
-                return false;
-            }
-            if (event.getAction() == KeyEvent.ACTION_UP) {
-                mContainerView.sendEarlyUserCanceled();
-                mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
-            }
-            return true;
-        });
-
-        setOnApplyWindowInsetsListener(this);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        mPasswordField.setTextOperationUser(UserHandle.of(mUserId));
-        if (mCredentialType == Utils.CREDENTIAL_PIN) {
-            mPasswordField.setInputType(
-                    InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
-        }
-
-        mPasswordField.requestFocus();
-        mPasswordField.scheduleShowSoftInput();
-    }
-
-    @Override
-    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-        // Check if this was the result of hitting the enter key
-        final boolean isSoftImeEvent = event == null
-                && (actionId == EditorInfo.IME_NULL
-                || actionId == EditorInfo.IME_ACTION_DONE
-                || actionId == EditorInfo.IME_ACTION_NEXT);
-        final boolean isKeyboardEnterKey = event != null
-                && KeyEvent.isConfirmKey(event.getKeyCode())
-                && event.getAction() == KeyEvent.ACTION_DOWN;
-        if (isSoftImeEvent || isKeyboardEnterKey) {
-            checkPasswordAndUnlock();
-            return true;
-        }
-        return false;
-    }
-
-    private void checkPasswordAndUnlock() {
-        try (LockscreenCredential password = mCredentialType == Utils.CREDENTIAL_PIN
-                ? LockscreenCredential.createPinOrNone(mPasswordField.getText())
-                : LockscreenCredential.createPasswordOrNone(mPasswordField.getText())) {
-            if (password.isNone()) {
-                return;
-            }
-
-            // Request LockSettingsService to return the Gatekeeper Password in the
-            // VerifyCredentialResponse so that we can request a Gatekeeper HAT with the
-            // Gatekeeper Password and operationId.
-            mPendingLockCheck = LockPatternChecker.verifyCredential(mLockPatternUtils,
-                    password, mEffectiveUserId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
-                    this::onCredentialVerified);
-        }
-    }
-
-    @Override
-    protected void onCredentialVerified(@NonNull VerifyCredentialResponse response,
-            int timeoutMs) {
-        super.onCredentialVerified(response, timeoutMs);
-
-        if (response.isMatched()) {
-            mImm.hideSoftInputFromWindow(getWindowToken(), 0 /* flags */);
-        } else {
-            mPasswordField.setText("");
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        if (mAuthCredentialInput == null || mAuthCredentialHeader == null || mSubtitleView == null
-                || mDescriptionView == null || mPasswordField == null || mErrorView == null) {
-            return;
-        }
-
-        int inputLeftBound;
-        int inputTopBound;
-        int headerRightBound = right;
-        int headerTopBounds = top;
-        final int subTitleBottom = (mSubtitleView.getVisibility() == GONE) ? mTitleView.getBottom()
-                : mSubtitleView.getBottom();
-        final int descBottom = (mDescriptionView.getVisibility() == GONE) ? subTitleBottom
-                : mDescriptionView.getBottom();
-        if (getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
-            inputTopBound = (bottom - mAuthCredentialInput.getHeight()) / 2;
-            inputLeftBound = (right - left) / 2;
-            headerRightBound = inputLeftBound;
-            headerTopBounds -= Math.min(mIconView.getBottom(), mBottomInset);
-        } else {
-            inputTopBound =
-                    descBottom + (bottom - descBottom - mAuthCredentialInput.getHeight()) / 2;
-            inputLeftBound = (right - left - mAuthCredentialInput.getWidth()) / 2;
-        }
-
-        if (mDescriptionView.getBottom() > mBottomInset) {
-            mAuthCredentialHeader.layout(left, headerTopBounds, headerRightBound, bottom);
-        }
-        mAuthCredentialInput.layout(inputLeftBound, inputTopBound, right, bottom);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        final int newWidth = MeasureSpec.getSize(widthMeasureSpec);
-        final int newHeight = MeasureSpec.getSize(heightMeasureSpec) - mBottomInset;
-
-        setMeasuredDimension(newWidth, newHeight);
-
-        final int halfWidthSpec = MeasureSpec.makeMeasureSpec(getWidth() / 2,
-                MeasureSpec.AT_MOST);
-        final int fullHeightSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.UNSPECIFIED);
-        if (getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
-            measureChildren(halfWidthSpec, fullHeightSpec);
-        } else {
-            measureChildren(widthMeasureSpec, fullHeightSpec);
-        }
-    }
-
-    @NonNull
-    @Override
-    public WindowInsets onApplyWindowInsets(@NonNull View v, WindowInsets insets) {
-
-        final Insets bottomInset = insets.getInsets(ime());
-        if (v instanceof AuthCredentialPasswordView && mBottomInset != bottomInset.bottom) {
-            mBottomInset = bottomInset.bottom;
-            if (mBottomInset > 0
-                    && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
-                mTitleView.setSingleLine(true);
-                mTitleView.setEllipsize(TextUtils.TruncateAt.MARQUEE);
-                mTitleView.setMarqueeRepeatLimit(-1);
-                // select to enable marquee unless a screen reader is enabled
-                mTitleView.setSelected(!mAccessibilityManager.isEnabled()
-                        || !mAccessibilityManager.isTouchExplorationEnabled());
-            } else {
-                mTitleView.setSingleLine(false);
-                mTitleView.setEllipsize(null);
-                // select to enable marquee unless a screen reader is enabled
-                mTitleView.setSelected(false);
-            }
-            requestLayout();
-        }
-        return insets;
-    }
-
-    @Override
-    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println(TAG + "State:");
-        pw.println("  mBottomInset=" + mBottomInset);
-        pw.println("  mAuthCredentialHeader size=(" + mAuthCredentialHeader.getWidth() + ","
-                + mAuthCredentialHeader.getHeight());
-        pw.println("  mAuthCredentialInput size=(" + mAuthCredentialInput.getWidth() + ","
-                + mAuthCredentialInput.getHeight());
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
deleted file mode 100644
index f9e44a0..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.util.AttributeSet;
-
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockPatternView;
-import com.android.internal.widget.LockscreenCredential;
-import com.android.internal.widget.VerifyCredentialResponse;
-import com.android.systemui.R;
-
-import java.util.List;
-
-/**
- * Pattern UI
- */
-public class AuthCredentialPatternView extends AuthCredentialView {
-
-    private LockPatternView mLockPatternView;
-
-    private class UnlockPatternListener implements LockPatternView.OnPatternListener {
-
-        @Override
-        public void onPatternStart() {
-
-        }
-
-        @Override
-        public void onPatternCleared() {
-
-        }
-
-        @Override
-        public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
-
-        }
-
-        @Override
-        public void onPatternDetected(List<LockPatternView.Cell> pattern) {
-            if (mPendingLockCheck != null) {
-                mPendingLockCheck.cancel(false);
-            }
-
-            mLockPatternView.setEnabled(false);
-
-            if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
-                // Pattern size is less than the minimum, do not count it as a failed attempt.
-                onPatternVerified(VerifyCredentialResponse.ERROR, 0 /* timeoutMs */);
-                return;
-            }
-
-            try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) {
-                // Request LockSettingsService to return the Gatekeeper Password in the
-                // VerifyCredentialResponse so that we can request a Gatekeeper HAT with the
-                // Gatekeeper Password and operationId.
-                mPendingLockCheck = LockPatternChecker.verifyCredential(
-                        mLockPatternUtils,
-                        credential,
-                        mEffectiveUserId,
-                        LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
-                        this::onPatternVerified);
-            }
-        }
-
-        private void onPatternVerified(@NonNull VerifyCredentialResponse response, int timeoutMs) {
-            AuthCredentialPatternView.this.onCredentialVerified(response, timeoutMs);
-            if (timeoutMs > 0) {
-                mLockPatternView.setEnabled(false);
-            } else {
-                mLockPatternView.setEnabled(true);
-            }
-        }
-    }
-
-    @Override
-    protected void onErrorTimeoutFinish() {
-        super.onErrorTimeoutFinish();
-        // select to enable marquee unless a screen reader is enabled
-        mLockPatternView.setEnabled(!mAccessibilityManager.isEnabled()
-                || !mAccessibilityManager.isTouchExplorationEnabled());
-    }
-
-    public AuthCredentialPatternView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mLockPatternView = findViewById(R.id.lockPattern);
-        mLockPatternView.setOnPatternListener(new UnlockPatternListener());
-        mLockPatternView.setInStealthMode(
-                !mLockPatternUtils.isVisiblePatternEnabled(mUserId));
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
deleted file mode 100644
index fa623d1..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ /dev/null
@@ -1,565 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS;
-import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT;
-import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT;
-import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT;
-import static android.app.admin.DevicePolicyResources.UNDEFINED;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AlertDialog;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.pm.UserInfo;
-import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.PromptInfo;
-import android.os.AsyncTask;
-import android.os.CountDownTimer;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.os.UserManager;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.StringRes;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.VerifyCredentialResponse;
-import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Abstract base class for Pin, Pattern, or Password authentication, for
- * {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)}}
- */
-public abstract class AuthCredentialView extends LinearLayout {
-    private static final String TAG = "BiometricPrompt/AuthCredentialView";
-    private static final int ERROR_DURATION_MS = 3000;
-
-    static final int USER_TYPE_PRIMARY = 1;
-    static final int USER_TYPE_MANAGED_PROFILE = 2;
-    static final int USER_TYPE_SECONDARY = 3;
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({USER_TYPE_PRIMARY, USER_TYPE_MANAGED_PROFILE, USER_TYPE_SECONDARY})
-    private @interface UserType {}
-
-    protected final Handler mHandler;
-    protected final LockPatternUtils mLockPatternUtils;
-
-    protected final AccessibilityManager mAccessibilityManager;
-    private final UserManager mUserManager;
-    private final DevicePolicyManager mDevicePolicyManager;
-
-    private PromptInfo mPromptInfo;
-    private AuthPanelController mPanelController;
-    private boolean mShouldAnimatePanel;
-    private boolean mShouldAnimateContents;
-
-    protected TextView mTitleView;
-    protected TextView mSubtitleView;
-    protected TextView mDescriptionView;
-    protected ImageView mIconView;
-    protected TextView mErrorView;
-
-    protected @Utils.CredentialType int mCredentialType;
-    protected AuthContainerView mContainerView;
-    protected Callback mCallback;
-    protected AsyncTask<?, ?, ?> mPendingLockCheck;
-    protected int mUserId;
-    protected long mOperationId;
-    protected int mEffectiveUserId;
-    protected ErrorTimer mErrorTimer;
-
-    protected @Background DelayableExecutor mBackgroundExecutor;
-
-    interface Callback {
-        void onCredentialMatched(byte[] attestation);
-    }
-
-    protected static class ErrorTimer extends CountDownTimer {
-        private final TextView mErrorView;
-        private final Context mContext;
-
-        /**
-         * @param millisInFuture    The number of millis in the future from the call
-         *                          to {@link #start()} until the countdown is done and {@link
-         *                          #onFinish()}
-         *                          is called.
-         * @param countDownInterval The interval along the way to receive
-         *                          {@link #onTick(long)} callbacks.
-         */
-        public ErrorTimer(Context context, long millisInFuture, long countDownInterval,
-                TextView errorView) {
-            super(millisInFuture, countDownInterval);
-            mErrorView = errorView;
-            mContext = context;
-        }
-
-        @Override
-        public void onTick(long millisUntilFinished) {
-            final int secondsCountdown = (int) (millisUntilFinished / 1000);
-            mErrorView.setText(mContext.getString(
-                    R.string.biometric_dialog_credential_too_many_attempts, secondsCountdown));
-        }
-
-        @Override
-        public void onFinish() {
-            if (mErrorView != null) {
-                mErrorView.setText("");
-            }
-        }
-    }
-
-    protected final Runnable mClearErrorRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (mErrorView != null) {
-                mErrorView.setText("");
-            }
-        }
-    };
-
-    public AuthCredentialView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        mLockPatternUtils = new LockPatternUtils(mContext);
-        mHandler = new Handler(Looper.getMainLooper());
-        mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
-        mUserManager = mContext.getSystemService(UserManager.class);
-        mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
-    }
-
-    protected void showError(String error) {
-        if (mHandler != null) {
-            mHandler.removeCallbacks(mClearErrorRunnable);
-            mHandler.postDelayed(mClearErrorRunnable, ERROR_DURATION_MS);
-        }
-        if (mErrorView != null) {
-            mErrorView.setText(error);
-        }
-    }
-
-    private void setTextOrHide(TextView view, CharSequence text) {
-        if (TextUtils.isEmpty(text)) {
-            view.setVisibility(View.GONE);
-        } else {
-            view.setText(text);
-        }
-
-        Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
-    }
-
-    private void setText(TextView view, CharSequence text) {
-        view.setText(text);
-    }
-
-    void setUserId(int userId) {
-        mUserId = userId;
-    }
-
-    void setOperationId(long operationId) {
-        mOperationId = operationId;
-    }
-
-    void setEffectiveUserId(int effectiveUserId) {
-        mEffectiveUserId = effectiveUserId;
-    }
-
-    void setCredentialType(@Utils.CredentialType int credentialType) {
-        mCredentialType = credentialType;
-    }
-
-    void setCallback(Callback callback) {
-        mCallback = callback;
-    }
-
-    void setPromptInfo(PromptInfo promptInfo) {
-        mPromptInfo = promptInfo;
-    }
-
-    void setPanelController(AuthPanelController panelController, boolean animatePanel) {
-        mPanelController = panelController;
-        mShouldAnimatePanel = animatePanel;
-    }
-
-    void setShouldAnimateContents(boolean animateContents) {
-        mShouldAnimateContents = animateContents;
-    }
-
-    void setContainerView(AuthContainerView containerView) {
-        mContainerView = containerView;
-    }
-
-    void setBackgroundExecutor(@Background DelayableExecutor bgExecutor) {
-        mBackgroundExecutor = bgExecutor;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        final CharSequence title = getTitle(mPromptInfo);
-        setText(mTitleView, title);
-        setTextOrHide(mSubtitleView, getSubtitle(mPromptInfo));
-        setTextOrHide(mDescriptionView, getDescription(mPromptInfo));
-        announceForAccessibility(title);
-
-        if (mIconView != null) {
-            final boolean isManagedProfile = Utils.isManagedProfile(mContext, mEffectiveUserId);
-            final Drawable image;
-            if (isManagedProfile) {
-                image = getResources().getDrawable(R.drawable.auth_dialog_enterprise,
-                        mContext.getTheme());
-            } else {
-                image = getResources().getDrawable(R.drawable.auth_dialog_lock,
-                        mContext.getTheme());
-            }
-            mIconView.setImageDrawable(image);
-        }
-
-        // Only animate this if we're transitioning from a biometric view.
-        if (mShouldAnimateContents) {
-            setTranslationY(getResources()
-                    .getDimension(R.dimen.biometric_dialog_credential_translation_offset));
-            setAlpha(0);
-
-            postOnAnimation(() -> {
-                animate().translationY(0)
-                        .setDuration(AuthDialog.ANIMATE_CREDENTIAL_INITIAL_DURATION_MS)
-                        .alpha(1.f)
-                        .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
-                        .withLayer()
-                        .start();
-            });
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mErrorTimer != null) {
-            mErrorTimer.cancel();
-        }
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mTitleView = findViewById(R.id.title);
-        mSubtitleView = findViewById(R.id.subtitle);
-        mDescriptionView = findViewById(R.id.description);
-        mIconView = findViewById(R.id.icon);
-        mErrorView = findViewById(R.id.error);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        if (mShouldAnimatePanel) {
-            // Credential view is always full screen.
-            mPanelController.setUseFullScreen(true);
-            mPanelController.updateForContentDimensions(mPanelController.getContainerWidth(),
-                    mPanelController.getContainerHeight(), 0 /* animateDurationMs */);
-            mShouldAnimatePanel = false;
-        }
-    }
-
-    protected void onErrorTimeoutFinish() {}
-
-    protected void onCredentialVerified(@NonNull VerifyCredentialResponse response, int timeoutMs) {
-        if (response.isMatched()) {
-            mClearErrorRunnable.run();
-            mLockPatternUtils.userPresent(mEffectiveUserId);
-
-            // The response passed into this method contains the Gatekeeper Password. We still
-            // have to request Gatekeeper to create a Hardware Auth Token with the
-            // Gatekeeper Password and Challenge (keystore operationId in this case)
-            final long pwHandle = response.getGatekeeperPasswordHandle();
-            final VerifyCredentialResponse gkResponse = mLockPatternUtils
-                    .verifyGatekeeperPasswordHandle(pwHandle, mOperationId, mEffectiveUserId);
-
-            mCallback.onCredentialMatched(gkResponse.getGatekeeperHAT());
-            mLockPatternUtils.removeGatekeeperPasswordHandle(pwHandle);
-        } else {
-            if (timeoutMs > 0) {
-                mHandler.removeCallbacks(mClearErrorRunnable);
-                long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
-                        mEffectiveUserId, timeoutMs);
-                mErrorTimer = new ErrorTimer(mContext,
-                        deadline - SystemClock.elapsedRealtime(),
-                        LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS,
-                        mErrorView) {
-                    @Override
-                    public void onFinish() {
-                        onErrorTimeoutFinish();
-                        mClearErrorRunnable.run();
-                    }
-                };
-                mErrorTimer.start();
-            } else {
-                final boolean didUpdateErrorText = reportFailedAttempt();
-                if (!didUpdateErrorText) {
-                    final @StringRes int errorRes;
-                    switch (mCredentialType) {
-                        case Utils.CREDENTIAL_PIN:
-                            errorRes = R.string.biometric_dialog_wrong_pin;
-                            break;
-                        case Utils.CREDENTIAL_PATTERN:
-                            errorRes = R.string.biometric_dialog_wrong_pattern;
-                            break;
-                        case Utils.CREDENTIAL_PASSWORD:
-                        default:
-                            errorRes = R.string.biometric_dialog_wrong_password;
-                            break;
-                    }
-                    showError(getResources().getString(errorRes));
-                }
-            }
-        }
-    }
-
-    private boolean reportFailedAttempt() {
-        boolean result = updateErrorMessage(
-                mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId) + 1);
-        mLockPatternUtils.reportFailedPasswordAttempt(mEffectiveUserId);
-        return result;
-    }
-
-    private boolean updateErrorMessage(int numAttempts) {
-        // Don't show any message if there's no maximum number of attempts.
-        final int maxAttempts = mLockPatternUtils.getMaximumFailedPasswordsForWipe(
-                mEffectiveUserId);
-        if (maxAttempts <= 0 || numAttempts <= 0) {
-            return false;
-        }
-
-        // Update the on-screen error string.
-        if (mErrorView != null) {
-            final String message = getResources().getString(
-                    R.string.biometric_dialog_credential_attempts_before_wipe,
-                    numAttempts,
-                    maxAttempts);
-            showError(message);
-        }
-
-        // Only show dialog if <=1 attempts are left before wiping.
-        final int remainingAttempts = maxAttempts - numAttempts;
-        if (remainingAttempts == 1) {
-            showLastAttemptBeforeWipeDialog();
-        } else if (remainingAttempts <= 0) {
-            showNowWipingDialog();
-        }
-        return true;
-    }
-
-    private void showLastAttemptBeforeWipeDialog() {
-        mBackgroundExecutor.execute(() -> {
-            final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
-                    .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title)
-                    .setMessage(
-                            getLastAttemptBeforeWipeMessage(getUserTypeForWipe(), mCredentialType))
-                    .setPositiveButton(android.R.string.ok, null)
-                    .create();
-            alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
-            mHandler.post(alertDialog::show);
-        });
-    }
-
-    private void showNowWipingDialog() {
-        mBackgroundExecutor.execute(() -> {
-            String nowWipingMessage = getNowWipingMessage(getUserTypeForWipe());
-            final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
-                    .setMessage(nowWipingMessage)
-                    .setPositiveButton(
-                            com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss,
-                            null /* OnClickListener */)
-                    .setOnDismissListener(
-                            dialog -> mContainerView.animateAway(
-                                    AuthDialogCallback.DISMISSED_ERROR))
-                    .create();
-            alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
-            mHandler.post(alertDialog::show);
-        });
-    }
-
-    private @UserType int getUserTypeForWipe() {
-        final UserInfo userToBeWiped = mUserManager.getUserInfo(
-                mDevicePolicyManager.getProfileWithMinimumFailedPasswordsForWipe(mEffectiveUserId));
-        if (userToBeWiped == null || userToBeWiped.isPrimary()) {
-            return USER_TYPE_PRIMARY;
-        } else if (userToBeWiped.isManagedProfile()) {
-            return USER_TYPE_MANAGED_PROFILE;
-        } else {
-            return USER_TYPE_SECONDARY;
-        }
-    }
-
-    // This should not be called on the main thread to avoid making an IPC.
-    private String getLastAttemptBeforeWipeMessage(
-            @UserType int userType, @Utils.CredentialType int credentialType) {
-        switch (userType) {
-            case USER_TYPE_PRIMARY:
-                return getLastAttemptBeforeWipeDeviceMessage(credentialType);
-            case USER_TYPE_MANAGED_PROFILE:
-                return getLastAttemptBeforeWipeProfileMessage(credentialType);
-            case USER_TYPE_SECONDARY:
-                return getLastAttemptBeforeWipeUserMessage(credentialType);
-            default:
-                throw new IllegalArgumentException("Unrecognized user type:" + userType);
-        }
-    }
-
-    private String getLastAttemptBeforeWipeDeviceMessage(
-            @Utils.CredentialType int credentialType) {
-        switch (credentialType) {
-            case Utils.CREDENTIAL_PIN:
-                return mContext.getString(
-                        R.string.biometric_dialog_last_pin_attempt_before_wipe_device);
-            case Utils.CREDENTIAL_PATTERN:
-                return mContext.getString(
-                        R.string.biometric_dialog_last_pattern_attempt_before_wipe_device);
-            case Utils.CREDENTIAL_PASSWORD:
-            default:
-                return mContext.getString(
-                        R.string.biometric_dialog_last_password_attempt_before_wipe_device);
-        }
-    }
-
-    // This should not be called on the main thread to avoid making an IPC.
-    private String getLastAttemptBeforeWipeProfileMessage(
-            @Utils.CredentialType int credentialType) {
-        return mDevicePolicyManager.getResources().getString(
-                getLastAttemptBeforeWipeProfileUpdatableStringId(credentialType),
-                () -> getLastAttemptBeforeWipeProfileDefaultMessage(credentialType));
-    }
-
-    private static String getLastAttemptBeforeWipeProfileUpdatableStringId(
-            @Utils.CredentialType int credentialType) {
-        switch (credentialType) {
-            case Utils.CREDENTIAL_PIN:
-                return BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT;
-            case Utils.CREDENTIAL_PATTERN:
-                return BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT;
-            case Utils.CREDENTIAL_PASSWORD:
-            default:
-                return BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT;
-        }
-    }
-
-    private String getLastAttemptBeforeWipeProfileDefaultMessage(
-            @Utils.CredentialType int credentialType) {
-        int resId;
-        switch (credentialType) {
-            case Utils.CREDENTIAL_PIN:
-                resId = R.string.biometric_dialog_last_pin_attempt_before_wipe_profile;
-                break;
-            case Utils.CREDENTIAL_PATTERN:
-                resId = R.string.biometric_dialog_last_pattern_attempt_before_wipe_profile;
-                break;
-            case Utils.CREDENTIAL_PASSWORD:
-            default:
-                resId = R.string.biometric_dialog_last_password_attempt_before_wipe_profile;
-        }
-        return mContext.getString(resId);
-    }
-
-    private String getLastAttemptBeforeWipeUserMessage(
-            @Utils.CredentialType int credentialType) {
-        int resId;
-        switch (credentialType) {
-            case Utils.CREDENTIAL_PIN:
-                resId = R.string.biometric_dialog_last_pin_attempt_before_wipe_user;
-                break;
-            case Utils.CREDENTIAL_PATTERN:
-                resId = R.string.biometric_dialog_last_pattern_attempt_before_wipe_user;
-                break;
-            case Utils.CREDENTIAL_PASSWORD:
-            default:
-                resId = R.string.biometric_dialog_last_password_attempt_before_wipe_user;
-        }
-        return mContext.getString(resId);
-    }
-
-    private String getNowWipingMessage(@UserType int userType) {
-        return mDevicePolicyManager.getResources().getString(
-                getNowWipingUpdatableStringId(userType),
-                () -> getNowWipingDefaultMessage(userType));
-    }
-
-    private String getNowWipingUpdatableStringId(@UserType int userType) {
-        switch (userType) {
-            case USER_TYPE_MANAGED_PROFILE:
-                return BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS;
-            default:
-                return UNDEFINED;
-        }
-    }
-
-    private String getNowWipingDefaultMessage(@UserType int userType) {
-        int resId;
-        switch (userType) {
-            case USER_TYPE_PRIMARY:
-                resId = com.android.settingslib.R.string.failed_attempts_now_wiping_device;
-                break;
-            case USER_TYPE_MANAGED_PROFILE:
-                resId = com.android.settingslib.R.string.failed_attempts_now_wiping_profile;
-                break;
-            case USER_TYPE_SECONDARY:
-                resId = com.android.settingslib.R.string.failed_attempts_now_wiping_user;
-                break;
-            default:
-                throw new IllegalArgumentException("Unrecognized user type:" + userType);
-        }
-        return mContext.getString(resId);
-    }
-
-    @Nullable
-    private static CharSequence getTitle(@NonNull PromptInfo promptInfo) {
-        final CharSequence credentialTitle = promptInfo.getDeviceCredentialTitle();
-        return credentialTitle != null ? credentialTitle : promptInfo.getTitle();
-    }
-
-    @Nullable
-    private static CharSequence getSubtitle(@NonNull PromptInfo promptInfo) {
-        final CharSequence credentialSubtitle = promptInfo.getDeviceCredentialSubtitle();
-        return credentialSubtitle != null ? credentialSubtitle : promptInfo.getSubtitle();
-    }
-
-    @Nullable
-    private static CharSequence getDescription(@NonNull PromptInfo promptInfo) {
-        final CharSequence credentialDescription = promptInfo.getDeviceCredentialDescription();
-        return credentialDescription != null ? credentialDescription : promptInfo.getDescription();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
index f1e42e0..5c616f0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
@@ -177,11 +177,11 @@
         }
     }
 
-    int getContainerWidth() {
+    public int getContainerWidth() {
         return mContainerWidth;
     }
 
-    int getContainerHeight() {
+    public int getContainerHeight() {
         return mContainerHeight;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 3273d74..48c60a0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -60,6 +60,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -119,6 +121,7 @@
     @NonNull private final SystemUIDialogManager mDialogManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @NonNull private final VibratorHelper mVibrator;
+    @NonNull private final FeatureFlags mFeatureFlags;
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
     @NonNull private final AccessibilityManager mAccessibilityManager;
@@ -202,6 +205,10 @@
         @Override
         public void showUdfpsOverlay(long requestId, int sensorId, int reason,
                 @NonNull IUdfpsOverlayControllerCallback callback) {
+            if (mFeatureFlags.isEnabled(Flags.NEW_UDFPS_OVERLAY)) {
+                return;
+            }
+
             mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(
                     new UdfpsControllerOverlay(mContext, mFingerprintManager, mInflater,
                             mWindowManager, mAccessibilityManager, mStatusBarStateController,
@@ -217,6 +224,10 @@
 
         @Override
         public void hideUdfpsOverlay(int sensorId) {
+            if (mFeatureFlags.isEnabled(Flags.NEW_UDFPS_OVERLAY)) {
+                return;
+            }
+
             mFgExecutor.execute(() -> {
                 if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
                     // if we get here, we expect keyguardUpdateMonitor's fingerprintRunningState
@@ -590,6 +601,7 @@
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             @NonNull DumpManager dumpManager,
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
+            @NonNull FeatureFlags featureFlags,
             @NonNull FalsingManager falsingManager,
             @NonNull PowerManager powerManager,
             @NonNull AccessibilityManager accessibilityManager,
@@ -625,6 +637,7 @@
         mDumpManager = dumpManager;
         mDialogManager = dialogManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mFeatureFlags = featureFlags;
         mFalsingManager = falsingManager;
         mPowerManager = powerManager;
         mAccessibilityManager = accessibilityManager;
@@ -859,9 +872,7 @@
             playStartHaptic();
 
             if (!mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
-                mKeyguardUpdateMonitor.requestFaceAuth(
-                        /* userInitiatedRequest */ false,
-                        FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
+                mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
             }
         }
         mOnFingerDown = true;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlay.kt
index 6e78f3d..142642a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlay.kt
@@ -19,26 +19,40 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.graphics.PixelFormat
+import android.graphics.Point
 import android.graphics.Rect
+import android.hardware.biometrics.BiometricOverlayConstants
 import android.hardware.fingerprint.FingerprintManager
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
+import android.hardware.fingerprint.IUdfpsOverlay
 import android.os.Handler
+import android.provider.Settings
 import android.view.MotionEvent
-import android.view.View
 import android.view.WindowManager
 import android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.concurrency.Execution
-import java.util.*
+import java.util.Optional
 import java.util.concurrent.Executor
 import javax.inject.Inject
+import kotlin.math.cos
+import kotlin.math.pow
+import kotlin.math.sin
 
 private const val TAG = "UdfpsOverlay"
 
+const val SETTING_OVERLAY_DEBUG = "udfps_overlay_debug"
+
+// Number of sensor points needed inside ellipse for good overlap
+private const val NEEDED_POINTS = 2
+
 @SuppressLint("ClickableViewAccessibility")
 @SysUISingleton
 class UdfpsOverlay
@@ -51,10 +65,11 @@
     private val handler: Handler,
     private val biometricExecutor: Executor,
     private val alternateTouchProvider: Optional<AlternateUdfpsTouchProvider>,
-    private val fgExecutor: DelayableExecutor,
+    @Main private val fgExecutor: DelayableExecutor,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val authController: AuthController,
-    private val udfpsLogger: UdfpsLogger
+    private val udfpsLogger: UdfpsLogger,
+    private var featureFlags: FeatureFlags
 ) : CoreStartable {
 
     /** The view, when [isShowing], or null. */
@@ -64,7 +79,11 @@
     private var requestId: Long = 0
     private var onFingerDown = false
     val size = windowManager.maximumWindowMetrics.bounds
+
     val udfpsProps: MutableList<FingerprintSensorPropertiesInternal> = mutableListOf()
+    var points: Array<Point> = emptyArray()
+    var processedMotionEvent = false
+    var isShowing = false
 
     private var params: UdfpsOverlayParams = UdfpsOverlayParams()
 
@@ -87,41 +106,140 @@
                 inputFeatures = INPUT_FEATURE_SPY
             }
 
-    fun onTouch(v: View, event: MotionEvent): Boolean {
-        val view = v as UdfpsOverlayView
+    fun onTouch(event: MotionEvent): Boolean {
+        val view = overlayView!!
 
         return when (event.action) {
             MotionEvent.ACTION_DOWN,
             MotionEvent.ACTION_MOVE -> {
                 onFingerDown = true
                 if (!view.isDisplayConfigured && alternateTouchProvider.isPresent) {
-                    biometricExecutor.execute {
-                        alternateTouchProvider
-                            .get()
-                            .onPointerDown(
-                                requestId,
-                                event.x.toInt(),
-                                event.y.toInt(),
-                                event.touchMinor,
-                                event.touchMajor
-                            )
-                    }
-                    fgExecutor.execute {
-                        if (keyguardUpdateMonitor.isFingerprintDetectionRunning) {
-                            keyguardUpdateMonitor.onUdfpsPointerDown(requestId.toInt())
+                    view.processMotionEvent(event)
+
+                    val goodOverlap =
+                        if (featureFlags.isEnabled(Flags.NEW_ELLIPSE_DETECTION)) {
+                            isGoodEllipseOverlap(event)
+                        } else {
+                            isGoodCentroidOverlap(event)
+                        }
+
+                    if (!processedMotionEvent && goodOverlap) {
+                        biometricExecutor.execute {
+                            alternateTouchProvider
+                                .get()
+                                .onPointerDown(
+                                    requestId,
+                                    event.rawX.toInt(),
+                                    event.rawY.toInt(),
+                                    event.touchMinor,
+                                    event.touchMajor
+                                )
+                        }
+                        fgExecutor.execute {
+                            if (keyguardUpdateMonitor.isFingerprintDetectionRunning) {
+                                keyguardUpdateMonitor.onUdfpsPointerDown(requestId.toInt())
+                            }
+
+                            view.configureDisplay {
+                                biometricExecutor.execute {
+                                    alternateTouchProvider.get().onUiReady()
+                                }
+                            }
+
+                            processedMotionEvent = true
                         }
                     }
 
-                    view.configureDisplay {
-                        biometricExecutor.execute { alternateTouchProvider.get().onUiReady() }
-                    }
+                    view.invalidate()
                 }
-
                 true
             }
             MotionEvent.ACTION_UP,
             MotionEvent.ACTION_CANCEL -> {
-                if (onFingerDown && alternateTouchProvider.isPresent) {
+                if (processedMotionEvent && alternateTouchProvider.isPresent) {
+                    biometricExecutor.execute {
+                        alternateTouchProvider.get().onPointerUp(requestId)
+                    }
+                    fgExecutor.execute {
+                        if (keyguardUpdateMonitor.isFingerprintDetectionRunning) {
+                            keyguardUpdateMonitor.onUdfpsPointerUp(requestId.toInt())
+                        }
+                    }
+
+                    processedMotionEvent = false
+                }
+
+                if (view.isDisplayConfigured) {
+                    view.unconfigureDisplay()
+                }
+
+                view.invalidate()
+                true
+            }
+            else -> false
+        }
+    }
+
+    fun isGoodEllipseOverlap(event: MotionEvent): Boolean {
+        return points.count { checkPoint(event, it) } >= NEEDED_POINTS
+    }
+
+    fun isGoodCentroidOverlap(event: MotionEvent): Boolean {
+        return params.sensorBounds.contains(event.rawX.toInt(), event.rawY.toInt())
+    }
+
+    fun checkPoint(event: MotionEvent, point: Point): Boolean {
+        // Calculate if sensor point is within ellipse
+        // Formula: ((cos(o)(xE - xS) + sin(o)(yE - yS))^2 / a^2) + ((sin(o)(xE - xS) + cos(o)(yE -
+        // yS))^2 / b^2) <= 1
+        val a: Float = cos(event.orientation) * (point.x - event.rawX)
+        val b: Float = sin(event.orientation) * (point.y - event.rawY)
+        val c: Float = sin(event.orientation) * (point.x - event.rawX)
+        val d: Float = cos(event.orientation) * (point.y - event.rawY)
+        val result =
+            (a + b).pow(2) / (event.touchMinor / 2).pow(2) +
+                (c - d).pow(2) / (event.touchMajor / 2).pow(2)
+
+        return result <= 1
+    }
+
+    fun show(requestId: Long) {
+        if (!featureFlags.isEnabled(Flags.NEW_UDFPS_OVERLAY)) {
+            return
+        }
+
+        this.requestId = requestId
+        fgExecutor.execute {
+            if (overlayView == null && alternateTouchProvider.isPresent) {
+                UdfpsOverlayView(context, null).let {
+                    it.overlayParams = params
+                    it.setUdfpsDisplayMode(
+                        UdfpsDisplayMode(context, execution, authController, udfpsLogger)
+                    )
+                    it.setOnTouchListener { _, event -> onTouch(event) }
+                    it.sensorPoints = points
+                    it.debugOverlay =
+                        Settings.Global.getInt(
+                            context.contentResolver,
+                            SETTING_OVERLAY_DEBUG,
+                            0 /* def */
+                        ) != 0
+                    overlayView = it
+                }
+                windowManager.addView(overlayView, coreLayoutParams)
+                isShowing = true
+            }
+        }
+    }
+
+    fun hide() {
+        if (!featureFlags.isEnabled(Flags.NEW_UDFPS_OVERLAY)) {
+            return
+        }
+
+        fgExecutor.execute {
+            if (overlayView != null && isShowing && alternateTouchProvider.isPresent) {
+                if (processedMotionEvent) {
                     biometricExecutor.execute {
                         alternateTouchProvider.get().onPointerUp(requestId)
                     }
@@ -131,44 +249,23 @@
                         }
                     }
                 }
-                onFingerDown = false
-                if (view.isDisplayConfigured) {
-                    view.unconfigureDisplay()
+
+                if (overlayView!!.isDisplayConfigured) {
+                    overlayView!!.unconfigureDisplay()
                 }
 
-                true
+                overlayView?.apply {
+                    windowManager.removeView(this)
+                    setOnTouchListener(null)
+                }
+
+                isShowing = false
+                overlayView = null
+                processedMotionEvent = false
             }
-            else -> false
         }
     }
 
-    fun show(requestId: Long): Boolean {
-        this.requestId = requestId
-        if (overlayView == null && alternateTouchProvider.isPresent) {
-            UdfpsOverlayView(context, null).let {
-                it.overlayParams = params
-                it.setUdfpsDisplayMode(
-                    UdfpsDisplayMode(context, execution, authController, udfpsLogger)
-                )
-                it.setOnTouchListener { v, event -> onTouch(v, event) }
-                overlayView = it
-            }
-            windowManager.addView(overlayView, coreLayoutParams)
-            return true
-        }
-
-        return false
-    }
-
-    fun hide() {
-        overlayView?.apply {
-            windowManager.removeView(this)
-            setOnTouchListener(null)
-        }
-
-        overlayView = null
-    }
-
     @Override
     override fun start() {
         fingerprintManager?.addAuthenticatorsRegisteredCallback(
@@ -180,6 +277,18 @@
                 }
             }
         )
+
+        fingerprintManager?.setUdfpsOverlay(
+            object : IUdfpsOverlay.Stub() {
+                override fun show(
+                    requestId: Long,
+                    sensorId: Int,
+                    @BiometricOverlayConstants.ShowReason reason: Int
+                ) = show(requestId)
+
+                override fun hide(sensorId: Int) = hide()
+            }
+        )
     }
 
     private fun handleAllFingerprintAuthenticatorsRegistered(
@@ -201,6 +310,24 @@
                     naturalDisplayHeight = size.height(),
                     scaleFactor = 1f
                 )
+
+            val sensorX = params.sensorBounds.centerX()
+            val sensorY = params.sensorBounds.centerY()
+            val cornerOffset: Int = params.sensorBounds.width() / 4
+            val sideOffset: Int = params.sensorBounds.width() / 3
+
+            points =
+                arrayOf(
+                    Point(sensorX - cornerOffset, sensorY - cornerOffset),
+                    Point(sensorX, sensorY - sideOffset),
+                    Point(sensorX + cornerOffset, sensorY - cornerOffset),
+                    Point(sensorX - sideOffset, sensorY),
+                    Point(sensorX, sensorY),
+                    Point(sensorX + sideOffset, sensorY),
+                    Point(sensorX - cornerOffset, sensorY + cornerOffset),
+                    Point(sensorX, sensorY + sideOffset),
+                    Point(sensorX + cornerOffset, sensorY + cornerOffset)
+                )
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayView.kt
index d371332..4e6a06b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayView.kt
@@ -20,26 +20,41 @@
 import android.graphics.Canvas
 import android.graphics.Color
 import android.graphics.Paint
+import android.graphics.Point
 import android.graphics.RectF
 import android.util.AttributeSet
+import android.view.MotionEvent
 import android.widget.FrameLayout
 
 private const val TAG = "UdfpsOverlayView"
+private const val POINT_SIZE = 10f
 
 class UdfpsOverlayView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
-
-    private val sensorRect = RectF()
     var overlayParams = UdfpsOverlayParams()
     private var mUdfpsDisplayMode: UdfpsDisplayMode? = null
 
+    var debugOverlay = false
+
     var overlayPaint = Paint()
     var sensorPaint = Paint()
+    var touchPaint = Paint()
+    var pointPaint = Paint()
     val centerPaint = Paint()
 
+    var oval = RectF()
+
     /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */
     var isDisplayConfigured: Boolean = false
         private set
 
+    var touchX: Float = 0f
+    var touchY: Float = 0f
+    var touchMinor: Float = 0f
+    var touchMajor: Float = 0f
+    var touchOrientation: Double = 0.0
+
+    var sensorPoints: Array<Point>? = null
+
     init {
         this.setWillNotDraw(false)
     }
@@ -47,24 +62,60 @@
     override fun onAttachedToWindow() {
         super.onAttachedToWindow()
 
-        overlayPaint.color = Color.argb(120, 255, 0, 0)
+        overlayPaint.color = Color.argb(100, 255, 0, 0)
         overlayPaint.style = Paint.Style.FILL
 
+        touchPaint.color = Color.argb(200, 255, 255, 255)
+        touchPaint.style = Paint.Style.FILL
+
         sensorPaint.color = Color.argb(150, 134, 204, 255)
         sensorPaint.style = Paint.Style.FILL
+
+        pointPaint.color = Color.WHITE
+        pointPaint.style = Paint.Style.FILL
     }
 
     override fun onDraw(canvas: Canvas) {
         super.onDraw(canvas)
 
-        canvas.drawRect(overlayParams.overlayBounds, overlayPaint)
-        canvas.drawRect(overlayParams.sensorBounds, sensorPaint)
+        if (debugOverlay) {
+            // Draw overlay and sensor bounds
+            canvas.drawRect(overlayParams.overlayBounds, overlayPaint)
+            canvas.drawRect(overlayParams.sensorBounds, sensorPaint)
+        }
+
+        // Draw sensor circle
         canvas.drawCircle(
             overlayParams.sensorBounds.exactCenterX(),
             overlayParams.sensorBounds.exactCenterY(),
             overlayParams.sensorBounds.width().toFloat() / 2,
             centerPaint
         )
+
+        if (debugOverlay) {
+            // Draw Points
+            sensorPoints?.forEach {
+                canvas.drawCircle(it.x.toFloat(), it.y.toFloat(), POINT_SIZE, pointPaint)
+            }
+
+            // Draw touch oval
+            canvas.save()
+            canvas.rotate(Math.toDegrees(touchOrientation).toFloat(), touchX, touchY)
+
+            oval.setEmpty()
+            oval.set(
+                touchX - touchMinor / 2,
+                touchY + touchMajor / 2,
+                touchX + touchMinor / 2,
+                touchY - touchMajor / 2
+            )
+
+            canvas.drawOval(oval, touchPaint)
+
+            // Draw center point
+            canvas.drawCircle(touchX, touchY, POINT_SIZE, centerPaint)
+            canvas.restore()
+        }
     }
 
     fun setUdfpsDisplayMode(udfpsDisplayMode: UdfpsDisplayMode?) {
@@ -80,4 +131,12 @@
         isDisplayConfigured = false
         mUdfpsDisplayMode?.disable(null /* onDisabled */)
     }
+
+    fun processMotionEvent(event: MotionEvent) {
+        touchX = event.rawX
+        touchY = event.rawY
+        touchMinor = event.touchMinor
+        touchMajor = event.touchMajor
+        touchOrientation = event.orientation.toDouble()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
index 75640b7..da50f1c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
@@ -62,7 +62,6 @@
         if (args.size == 1 && args[0] == "hide") {
             hideOverlay()
         } else if (args.size == 2 && args[0] == "udfpsOverlay" && args[1] == "show") {
-            hideOverlay()
             showUdfpsOverlay()
         } else if (args.size == 2 && args[0] == "udfpsOverlay" && args[1] == "hide") {
             hideUdfpsOverlay()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index b5d81f2..7c0c3b7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -16,32 +16,45 @@
 
 package com.android.systemui.biometrics.dagger
 
+import com.android.systemui.biometrics.data.repository.PromptRepository
+import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl
+import com.android.systemui.biometrics.domain.interactor.CredentialInteractor
+import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.util.concurrency.ThreadFactory
+import dagger.Binds
 import dagger.Module
 import dagger.Provides
 import java.util.concurrent.Executor
 import javax.inject.Qualifier
 
-/**
- * Dagger module for all things biometric.
- */
+/** Dagger module for all things biometric. */
 @Module
-object BiometricsModule {
+interface BiometricsModule {
 
-    /** Background [Executor] for HAL related operations. */
-    @Provides
+    @Binds
     @SysUISingleton
-    @JvmStatic
-    @BiometricsBackground
-    fun providesPluginExecutor(threadFactory: ThreadFactory): Executor =
-        threadFactory.buildExecutorOnNewThread("biometrics")
+    fun biometricPromptRepository(impl: PromptRepositoryImpl): PromptRepository
+
+    @Binds
+    @SysUISingleton
+    fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor
+
+    companion object {
+        /** Background [Executor] for HAL related operations. */
+        @Provides
+        @SysUISingleton
+        @JvmStatic
+        @BiometricsBackground
+        fun providesPluginExecutor(threadFactory: ThreadFactory): Executor =
+            threadFactory.buildExecutorOnNewThread("biometrics")
+    }
 }
 
 /**
- * Background executor for HAL operations that are latency sensitive but too
- * slow to run on the main thread. Prefer the shared executors, such as
- * [com.android.systemui.dagger.qualifiers.Background] when a HAL is not directly involved.
+ * Background executor for HAL operations that are latency sensitive but too slow to run on the main
+ * thread. Prefer the shared executors, such as [com.android.systemui.dagger.qualifiers.Background]
+ * when a HAL is not directly involved.
  */
 @Qualifier
 @MustBeDocumented
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/model/PromptKind.kt
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
copy to packages/SystemUI/src/com/android/systemui/biometrics/data/model/PromptKind.kt
index 581dafa3..e82646f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/model/PromptKind.kt
@@ -14,10 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.model
+package com.android.systemui.biometrics.data.model
 
-/** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
-enum class KeyguardQuickAffordancePosition {
-    BOTTOM_START,
-    BOTTOM_END,
+import com.android.systemui.biometrics.Utils
+
+// TODO(b/251476085): this should eventually replace Utils.CredentialType
+/** Credential options for biometric prompt. Shadows [Utils.CredentialType]. */
+enum class PromptKind {
+    ANY_BIOMETRIC,
+    PIN,
+    PATTERN,
+    PASSWORD,
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
new file mode 100644
index 0000000..92a13cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
@@ -0,0 +1,102 @@
+package com.android.systemui.biometrics.data.repository
+
+import android.hardware.biometrics.PromptInfo
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.data.model.PromptKind
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * A repository for the global state of BiometricPrompt.
+ *
+ * There is never more than one instance of the prompt at any given time.
+ */
+interface PromptRepository {
+
+    /** If the prompt is showing. */
+    val isShowing: Flow<Boolean>
+
+    /** The app-specific details to show in the prompt. */
+    val promptInfo: StateFlow<PromptInfo?>
+
+    /** The user that the prompt is for. */
+    val userId: StateFlow<Int?>
+
+    /** The gatekeeper challenge, if one is associated with this prompt. */
+    val challenge: StateFlow<Long?>
+
+    /** The kind of credential to use (biometric, pin, pattern, etc.). */
+    val kind: StateFlow<PromptKind>
+
+    /** Update the prompt configuration, which should be set before [isShowing]. */
+    fun setPrompt(
+        promptInfo: PromptInfo,
+        userId: Int,
+        gatekeeperChallenge: Long?,
+        kind: PromptKind = PromptKind.ANY_BIOMETRIC,
+    )
+
+    /** Unset the prompt info. */
+    fun unsetPrompt()
+}
+
+@SysUISingleton
+class PromptRepositoryImpl @Inject constructor(private val authController: AuthController) :
+    PromptRepository {
+
+    override val isShowing: Flow<Boolean> = conflatedCallbackFlow {
+        val callback =
+            object : AuthController.Callback {
+                override fun onBiometricPromptShown() =
+                    trySendWithFailureLogging(true, TAG, "set isShowing")
+
+                override fun onBiometricPromptDismissed() =
+                    trySendWithFailureLogging(false, TAG, "unset isShowing")
+            }
+        authController.addCallback(callback)
+        trySendWithFailureLogging(authController.isShowing, TAG, "update isShowing")
+        awaitClose { authController.removeCallback(callback) }
+    }
+
+    private val _promptInfo: MutableStateFlow<PromptInfo?> = MutableStateFlow(null)
+    override val promptInfo = _promptInfo.asStateFlow()
+
+    private val _challenge: MutableStateFlow<Long?> = MutableStateFlow(null)
+    override val challenge: StateFlow<Long?> = _challenge.asStateFlow()
+
+    private val _userId: MutableStateFlow<Int?> = MutableStateFlow(null)
+    override val userId = _userId.asStateFlow()
+
+    private val _kind: MutableStateFlow<PromptKind> = MutableStateFlow(PromptKind.ANY_BIOMETRIC)
+    override val kind = _kind.asStateFlow()
+
+    override fun setPrompt(
+        promptInfo: PromptInfo,
+        userId: Int,
+        gatekeeperChallenge: Long?,
+        kind: PromptKind,
+    ) {
+        _kind.value = kind
+        _userId.value = userId
+        _challenge.value = gatekeeperChallenge
+        _promptInfo.value = promptInfo
+    }
+
+    override fun unsetPrompt() {
+        _promptInfo.value = null
+        _userId.value = null
+        _challenge.value = null
+        _kind.value = PromptKind.ANY_BIOMETRIC
+    }
+
+    companion object {
+        private const val TAG = "BiometricPromptRepository"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
new file mode 100644
index 0000000..1f1a1b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
@@ -0,0 +1,282 @@
+package com.android.systemui.biometrics.domain.interactor
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResources
+import android.content.Context
+import android.os.UserManager
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockscreenCredential
+import com.android.internal.widget.VerifyCredentialResponse
+import com.android.systemui.R
+import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+
+/**
+ * A wrapper for [LockPatternUtils] to verify PIN, pattern, or password credentials.
+ *
+ * This class also uses the [DevicePolicyManager] to generate appropriate error messages when policy
+ * exceptions are raised (i.e. wipe device due to excessive failed attempts, etc.).
+ */
+interface CredentialInteractor {
+    /** If the user's pattern credential should be hidden */
+    fun isStealthModeActive(userId: Int): Boolean
+
+    /** Get the effective user id (profile owner, if one exists) */
+    fun getCredentialOwnerOrSelfId(userId: Int): Int
+
+    /**
+     * Verifies a credential and returns a stream of results.
+     *
+     * The final emitted value will either be a [CredentialStatus.Fail.Error] or a
+     * [CredentialStatus.Success.Verified].
+     */
+    fun verifyCredential(
+        request: BiometricPromptRequest.Credential,
+        credential: LockscreenCredential,
+    ): Flow<CredentialStatus>
+}
+
+/** Standard implementation of [CredentialInteractor]. */
+class CredentialInteractorImpl
+@Inject
+constructor(
+    @Application private val applicationContext: Context,
+    private val lockPatternUtils: LockPatternUtils,
+    private val userManager: UserManager,
+    private val devicePolicyManager: DevicePolicyManager,
+    private val systemClock: SystemClock,
+) : CredentialInteractor {
+
+    override fun isStealthModeActive(userId: Int): Boolean =
+        !lockPatternUtils.isVisiblePatternEnabled(userId)
+
+    override fun getCredentialOwnerOrSelfId(userId: Int): Int =
+        userManager.getCredentialOwnerProfile(userId)
+
+    override fun verifyCredential(
+        request: BiometricPromptRequest.Credential,
+        credential: LockscreenCredential,
+    ): Flow<CredentialStatus> = flow {
+        // Request LockSettingsService to return the Gatekeeper Password in the
+        // VerifyCredentialResponse so that we can request a Gatekeeper HAT with the
+        // Gatekeeper Password and operationId.
+        val effectiveUserId = request.userInfo.deviceCredentialOwnerId
+        val response =
+            lockPatternUtils.verifyCredential(
+                credential,
+                effectiveUserId,
+                LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE
+            )
+
+        if (response.isMatched) {
+            lockPatternUtils.userPresent(effectiveUserId)
+
+            // The response passed into this method contains the Gatekeeper
+            // Password. We still have to request Gatekeeper to create a
+            // Hardware Auth Token with the Gatekeeper Password and Challenge
+            // (keystore operationId in this case)
+            val pwHandle = response.gatekeeperPasswordHandle
+            val gkResponse: VerifyCredentialResponse =
+                lockPatternUtils.verifyGatekeeperPasswordHandle(
+                    pwHandle,
+                    request.operationInfo.gatekeeperChallenge,
+                    effectiveUserId
+                )
+            val hat = gkResponse.gatekeeperHAT
+            lockPatternUtils.removeGatekeeperPasswordHandle(pwHandle)
+            emit(CredentialStatus.Success.Verified(hat))
+        } else if (response.timeout > 0) {
+            // if requests are being throttled, update the error message every
+            // second until the temporary lock has expired
+            val deadline: Long =
+                lockPatternUtils.setLockoutAttemptDeadline(effectiveUserId, response.timeout)
+            val interval = LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS
+            var remaining = deadline - systemClock.elapsedRealtime()
+            while (remaining > 0) {
+                emit(
+                    CredentialStatus.Fail.Throttled(
+                        applicationContext.getString(
+                            R.string.biometric_dialog_credential_too_many_attempts,
+                            remaining / 1000
+                        )
+                    )
+                )
+                delay(interval)
+                remaining -= interval
+            }
+            emit(CredentialStatus.Fail.Error(""))
+        } else { // bad request, but not throttled
+            val numAttempts = lockPatternUtils.getCurrentFailedPasswordAttempts(effectiveUserId) + 1
+            val maxAttempts = lockPatternUtils.getMaximumFailedPasswordsForWipe(effectiveUserId)
+            if (maxAttempts <= 0 || numAttempts <= 0) {
+                // use a generic message if there's no maximum number of attempts
+                emit(CredentialStatus.Fail.Error())
+            } else {
+                val remainingAttempts = (maxAttempts - numAttempts).coerceAtLeast(0)
+                emit(
+                    CredentialStatus.Fail.Error(
+                        applicationContext.getString(
+                            R.string.biometric_dialog_credential_attempts_before_wipe,
+                            numAttempts,
+                            maxAttempts
+                        ),
+                        remainingAttempts,
+                        fetchFinalAttemptMessageOrNull(request, remainingAttempts)
+                    )
+                )
+            }
+            lockPatternUtils.reportFailedPasswordAttempt(effectiveUserId)
+        }
+    }
+
+    private fun fetchFinalAttemptMessageOrNull(
+        request: BiometricPromptRequest.Credential,
+        remainingAttempts: Int?,
+    ): String? =
+        if (remainingAttempts != null && remainingAttempts <= 1) {
+            applicationContext.getFinalAttemptMessageOrBlank(
+                request,
+                devicePolicyManager,
+                userManager.getUserTypeForWipe(
+                    devicePolicyManager,
+                    request.userInfo.deviceCredentialOwnerId
+                ),
+                remainingAttempts
+            )
+        } else {
+            null
+        }
+}
+
+private enum class UserType {
+    PRIMARY,
+    MANAGED_PROFILE,
+    SECONDARY,
+}
+
+private fun UserManager.getUserTypeForWipe(
+    devicePolicyManager: DevicePolicyManager,
+    effectiveUserId: Int,
+): UserType {
+    val userToBeWiped =
+        getUserInfo(
+            devicePolicyManager.getProfileWithMinimumFailedPasswordsForWipe(effectiveUserId)
+        )
+    return when {
+        userToBeWiped == null || userToBeWiped.isPrimary -> UserType.PRIMARY
+        userToBeWiped.isManagedProfile -> UserType.MANAGED_PROFILE
+        else -> UserType.SECONDARY
+    }
+}
+
+private fun Context.getFinalAttemptMessageOrBlank(
+    request: BiometricPromptRequest.Credential,
+    devicePolicyManager: DevicePolicyManager,
+    userType: UserType,
+    remaining: Int,
+): String =
+    when {
+        remaining == 1 -> getLastAttemptBeforeWipeMessage(request, devicePolicyManager, userType)
+        remaining <= 0 -> getNowWipingMessage(devicePolicyManager, userType)
+        else -> ""
+    }
+
+private fun Context.getLastAttemptBeforeWipeMessage(
+    request: BiometricPromptRequest.Credential,
+    devicePolicyManager: DevicePolicyManager,
+    userType: UserType,
+): String =
+    when (userType) {
+        UserType.PRIMARY -> getLastAttemptBeforeWipeDeviceMessage(request)
+        UserType.MANAGED_PROFILE ->
+            getLastAttemptBeforeWipeProfileMessage(request, devicePolicyManager)
+        UserType.SECONDARY -> getLastAttemptBeforeWipeUserMessage(request)
+    }
+
+private fun Context.getLastAttemptBeforeWipeDeviceMessage(
+    request: BiometricPromptRequest.Credential,
+): String {
+    val id =
+        when (request) {
+            is BiometricPromptRequest.Credential.Pin ->
+                R.string.biometric_dialog_last_pin_attempt_before_wipe_device
+            is BiometricPromptRequest.Credential.Pattern ->
+                R.string.biometric_dialog_last_pattern_attempt_before_wipe_device
+            is BiometricPromptRequest.Credential.Password ->
+                R.string.biometric_dialog_last_password_attempt_before_wipe_device
+        }
+    return getString(id)
+}
+
+private fun Context.getLastAttemptBeforeWipeProfileMessage(
+    request: BiometricPromptRequest.Credential,
+    devicePolicyManager: DevicePolicyManager,
+): String {
+    val id =
+        when (request) {
+            is BiometricPromptRequest.Credential.Pin ->
+                DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT
+            is BiometricPromptRequest.Credential.Pattern ->
+                DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT
+            is BiometricPromptRequest.Credential.Password ->
+                DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT
+        }
+    return devicePolicyManager.resources.getString(id) {
+        // use fallback a string if not found
+        val defaultId =
+            when (request) {
+                is BiometricPromptRequest.Credential.Pin ->
+                    R.string.biometric_dialog_last_pin_attempt_before_wipe_profile
+                is BiometricPromptRequest.Credential.Pattern ->
+                    R.string.biometric_dialog_last_pattern_attempt_before_wipe_profile
+                is BiometricPromptRequest.Credential.Password ->
+                    R.string.biometric_dialog_last_password_attempt_before_wipe_profile
+            }
+        getString(defaultId)
+    }
+}
+
+private fun Context.getLastAttemptBeforeWipeUserMessage(
+    request: BiometricPromptRequest.Credential,
+): String {
+    val resId =
+        when (request) {
+            is BiometricPromptRequest.Credential.Pin ->
+                R.string.biometric_dialog_last_pin_attempt_before_wipe_user
+            is BiometricPromptRequest.Credential.Pattern ->
+                R.string.biometric_dialog_last_pattern_attempt_before_wipe_user
+            is BiometricPromptRequest.Credential.Password ->
+                R.string.biometric_dialog_last_password_attempt_before_wipe_user
+        }
+    return getString(resId)
+}
+
+private fun Context.getNowWipingMessage(
+    devicePolicyManager: DevicePolicyManager,
+    userType: UserType,
+): String {
+    val id =
+        when (userType) {
+            UserType.MANAGED_PROFILE ->
+                DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS
+            else -> DevicePolicyResources.UNDEFINED
+        }
+    return devicePolicyManager.resources.getString(id) {
+        // use fallback a string if not found
+        val defaultId =
+            when (userType) {
+                UserType.PRIMARY ->
+                    com.android.settingslib.R.string.failed_attempts_now_wiping_device
+                UserType.MANAGED_PROFILE ->
+                    com.android.settingslib.R.string.failed_attempts_now_wiping_profile
+                UserType.SECONDARY ->
+                    com.android.settingslib.R.string.failed_attempts_now_wiping_user
+            }
+        getString(defaultId)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialStatus.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialStatus.kt
new file mode 100644
index 0000000..40b7612
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialStatus.kt
@@ -0,0 +1,23 @@
+package com.android.systemui.biometrics.domain.interactor
+
+/** Result of a [CredentialInteractor.verifyCredential] check. */
+sealed interface CredentialStatus {
+    /** A successful result. */
+    sealed interface Success : CredentialStatus {
+        /** The credential is valid and a [hat] has been generated. */
+        data class Verified(val hat: ByteArray) : Success
+    }
+    /** A failed result. */
+    sealed interface Fail : CredentialStatus {
+        val error: String?
+
+        /** The credential check failed with an [error]. */
+        data class Error(
+            override val error: String? = null,
+            val remainingAttempts: Int? = null,
+            val urgentMessage: String? = null,
+        ) : Fail
+        /** The credential check failed with an [error] and is temporarily locked out. */
+        data class Throttled(override val error: String) : Fail
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
new file mode 100644
index 0000000..6362c2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
@@ -0,0 +1,189 @@
+package com.android.systemui.biometrics.domain.interactor
+
+import android.hardware.biometrics.PromptInfo
+import com.android.internal.widget.LockPatternView
+import com.android.internal.widget.LockscreenCredential
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.data.model.PromptKind
+import com.android.systemui.biometrics.data.repository.PromptRepository
+import com.android.systemui.biometrics.domain.model.BiometricOperationInfo
+import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
+import com.android.systemui.biometrics.domain.model.BiometricUserInfo
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.lastOrNull
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.withContext
+
+/**
+ * Business logic for BiometricPrompt's CredentialViews, which primarily includes checking a users
+ * PIN, pattern, or password credential instead of a biometric.
+ */
+class BiometricPromptCredentialInteractor
+@Inject
+constructor(
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    private val biometricPromptRepository: PromptRepository,
+    private val credentialInteractor: CredentialInteractor,
+) {
+    /** If the prompt is currently showing. */
+    val isShowing: Flow<Boolean> = biometricPromptRepository.isShowing
+
+    /** Metadata about the current credential prompt, including app-supplied preferences. */
+    val prompt: Flow<BiometricPromptRequest?> =
+        combine(
+                biometricPromptRepository.promptInfo,
+                biometricPromptRepository.challenge,
+                biometricPromptRepository.userId,
+                biometricPromptRepository.kind
+            ) { promptInfo, challenge, userId, kind ->
+                if (promptInfo == null || userId == null || challenge == null) {
+                    return@combine null
+                }
+
+                when (kind) {
+                    PromptKind.PIN ->
+                        BiometricPromptRequest.Credential.Pin(
+                            info = promptInfo,
+                            userInfo = userInfo(userId),
+                            operationInfo = operationInfo(challenge)
+                        )
+                    PromptKind.PATTERN ->
+                        BiometricPromptRequest.Credential.Pattern(
+                            info = promptInfo,
+                            userInfo = userInfo(userId),
+                            operationInfo = operationInfo(challenge),
+                            stealthMode = credentialInteractor.isStealthModeActive(userId)
+                        )
+                    PromptKind.PASSWORD ->
+                        BiometricPromptRequest.Credential.Password(
+                            info = promptInfo,
+                            userInfo = userInfo(userId),
+                            operationInfo = operationInfo(challenge)
+                        )
+                    else -> null
+                }
+            }
+            .distinctUntilChanged()
+
+    private fun userInfo(userId: Int): BiometricUserInfo =
+        BiometricUserInfo(
+            userId = userId,
+            deviceCredentialOwnerId = credentialInteractor.getCredentialOwnerOrSelfId(userId)
+        )
+
+    private fun operationInfo(challenge: Long): BiometricOperationInfo =
+        BiometricOperationInfo(gatekeeperChallenge = challenge)
+
+    /** Most recent error due to [verifyCredential]. */
+    private val _verificationError = MutableStateFlow<CredentialStatus.Fail?>(null)
+    val verificationError: Flow<CredentialStatus.Fail?> = _verificationError.asStateFlow()
+
+    /** Update the current request to use credential-based authentication instead of biometrics. */
+    fun useCredentialsForAuthentication(
+        promptInfo: PromptInfo,
+        @Utils.CredentialType kind: Int,
+        userId: Int,
+        challenge: Long,
+    ) {
+        biometricPromptRepository.setPrompt(
+            promptInfo,
+            userId,
+            challenge,
+            kind.asBiometricPromptCredential()
+        )
+    }
+
+    /** Unset the current authentication request. */
+    fun resetPrompt() {
+        biometricPromptRepository.unsetPrompt()
+    }
+
+    /**
+     * Check a credential and return the attestation token (HAT) if successful.
+     *
+     * This method will not return if credential checks are being throttled until the throttling has
+     * expired and the user can try again. It will periodically update the [verificationError] until
+     * cancelled or the throttling has completed. If the request is not throttled, but unsuccessful,
+     * the [verificationError] will be set and an optional
+     * [CredentialStatus.Fail.Error.urgentMessage] message may be provided to indicate additional
+     * hints to the user (i.e. device will be wiped on next failure, etc.).
+     *
+     * The check happens on the background dispatcher given in the constructor.
+     */
+    suspend fun checkCredential(
+        request: BiometricPromptRequest.Credential,
+        text: CharSequence? = null,
+        pattern: List<LockPatternView.Cell>? = null,
+    ): CredentialStatus =
+        withContext(bgDispatcher) {
+            val credential =
+                when (request) {
+                    is BiometricPromptRequest.Credential.Pin ->
+                        LockscreenCredential.createPinOrNone(text ?: "")
+                    is BiometricPromptRequest.Credential.Password ->
+                        LockscreenCredential.createPasswordOrNone(text ?: "")
+                    is BiometricPromptRequest.Credential.Pattern ->
+                        LockscreenCredential.createPattern(pattern ?: listOf())
+                }
+
+            credential.use { c -> verifyCredential(request, c) }
+        }
+
+    private suspend fun verifyCredential(
+        request: BiometricPromptRequest.Credential,
+        credential: LockscreenCredential?
+    ): CredentialStatus {
+        if (credential == null || credential.isNone) {
+            return CredentialStatus.Fail.Error()
+        }
+
+        val finalStatus =
+            credentialInteractor
+                .verifyCredential(request, credential)
+                .onEach { status ->
+                    when (status) {
+                        is CredentialStatus.Success -> _verificationError.value = null
+                        is CredentialStatus.Fail -> _verificationError.value = status
+                    }
+                }
+                .lastOrNull()
+
+        return finalStatus ?: CredentialStatus.Fail.Error()
+    }
+
+    /**
+     * Report a user-visible error.
+     *
+     * Use this instead of calling [verifyCredential] when it is not necessary because the check
+     * will obviously fail (i.e. too short, empty, etc.)
+     */
+    fun setVerificationError(error: CredentialStatus.Fail.Error?) {
+        if (error != null) {
+            _verificationError.value = error
+        } else {
+            resetVerificationError()
+        }
+    }
+
+    /** Clear the current error message, if any. */
+    fun resetVerificationError() {
+        _verificationError.value = null
+    }
+}
+
+// TODO(b/251476085): remove along with Utils.CredentialType
+/** Convert a [Utils.CredentialType] to the corresponding [PromptKind]. */
+private fun @receiver:Utils.CredentialType Int.asBiometricPromptCredential(): PromptKind =
+    when (this) {
+        Utils.CREDENTIAL_PIN -> PromptKind.PIN
+        Utils.CREDENTIAL_PASSWORD -> PromptKind.PASSWORD
+        Utils.CREDENTIAL_PATTERN -> PromptKind.PATTERN
+        else -> PromptKind.ANY_BIOMETRIC
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricOperationInfo.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricOperationInfo.kt
new file mode 100644
index 0000000..c619b12
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricOperationInfo.kt
@@ -0,0 +1,4 @@
+package com.android.systemui.biometrics.domain.model
+
+/** Metadata about an in-progress biometric operation. */
+data class BiometricOperationInfo(val gatekeeperChallenge: Long = -1)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
new file mode 100644
index 0000000..5ee0381
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -0,0 +1,69 @@
+package com.android.systemui.biometrics.domain.model
+
+import android.hardware.biometrics.PromptInfo
+
+/**
+ * Preferences for BiometricPrompt, such as title & description, that are immutable while the prompt
+ * is showing.
+ *
+ * This roughly corresponds to a "request" by the system or an app to show BiometricPrompt and it
+ * contains a subset of the information in a [PromptInfo] that is relevant to SysUI.
+ */
+sealed class BiometricPromptRequest(
+    val title: String,
+    val subtitle: String,
+    val description: String,
+    val userInfo: BiometricUserInfo,
+    val operationInfo: BiometricOperationInfo,
+) {
+    /** Prompt using one or more biometrics. */
+    class Biometric(
+        info: PromptInfo,
+        userInfo: BiometricUserInfo,
+        operationInfo: BiometricOperationInfo,
+    ) :
+        BiometricPromptRequest(
+            title = info.title?.toString() ?: "",
+            subtitle = info.subtitle?.toString() ?: "",
+            description = info.description?.toString() ?: "",
+            userInfo = userInfo,
+            operationInfo = operationInfo
+        )
+
+    /** Prompt using a credential (pin, pattern, password). */
+    sealed class Credential(
+        info: PromptInfo,
+        userInfo: BiometricUserInfo,
+        operationInfo: BiometricOperationInfo,
+    ) :
+        BiometricPromptRequest(
+            title = (info.deviceCredentialTitle ?: info.title)?.toString() ?: "",
+            subtitle = (info.deviceCredentialSubtitle ?: info.subtitle)?.toString() ?: "",
+            description = (info.deviceCredentialDescription ?: info.description)?.toString() ?: "",
+            userInfo = userInfo,
+            operationInfo = operationInfo,
+        ) {
+
+        /** PIN prompt. */
+        class Pin(
+            info: PromptInfo,
+            userInfo: BiometricUserInfo,
+            operationInfo: BiometricOperationInfo,
+        ) : Credential(info, userInfo, operationInfo)
+
+        /** Password prompt. */
+        class Password(
+            info: PromptInfo,
+            userInfo: BiometricUserInfo,
+            operationInfo: BiometricOperationInfo,
+        ) : Credential(info, userInfo, operationInfo)
+
+        /** Pattern prompt. */
+        class Pattern(
+            info: PromptInfo,
+            userInfo: BiometricUserInfo,
+            operationInfo: BiometricOperationInfo,
+            val stealthMode: Boolean,
+        ) : Credential(info, userInfo, operationInfo)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricUserInfo.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricUserInfo.kt
new file mode 100644
index 0000000..08da04d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricUserInfo.kt
@@ -0,0 +1,7 @@
+package com.android.systemui.biometrics.domain.model
+
+/** Metadata about the current user BiometricPrompt is being shown to. */
+data class BiometricUserInfo(
+    val userId: Int,
+    val deviceCredentialOwnerId: Int = userId,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
new file mode 100644
index 0000000..bcc0575
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -0,0 +1,130 @@
+package com.android.systemui.biometrics.ui
+
+import android.content.Context
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.text.TextUtils
+import android.util.AttributeSet
+import android.view.View
+import android.view.WindowInsets
+import android.view.WindowInsets.Type.ime
+import android.view.accessibility.AccessibilityManager
+import android.widget.ImageView
+import android.widget.ImeAwareEditText
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.core.view.isGone
+import com.android.systemui.R
+import com.android.systemui.biometrics.AuthPanelController
+import com.android.systemui.biometrics.ui.binder.CredentialViewBinder
+import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
+
+/** PIN or password credential view for BiometricPrompt. */
+class CredentialPasswordView(context: Context, attrs: AttributeSet?) :
+    LinearLayout(context, attrs), CredentialView, View.OnApplyWindowInsetsListener {
+
+    private lateinit var titleView: TextView
+    private lateinit var subtitleView: TextView
+    private lateinit var descriptionView: TextView
+    private lateinit var iconView: ImageView
+    private lateinit var passwordField: ImeAwareEditText
+    private lateinit var credentialHeader: View
+    private lateinit var credentialInput: View
+
+    private var bottomInset: Int = 0
+
+    private val accessibilityManager by lazy {
+        context.getSystemService(AccessibilityManager::class.java)
+    }
+
+    /** Initializes the view. */
+    override fun init(
+        viewModel: CredentialViewModel,
+        host: CredentialView.Host,
+        panelViewController: AuthPanelController,
+        animatePanel: Boolean,
+    ) {
+        CredentialViewBinder.bind(this, host, viewModel, panelViewController, animatePanel)
+    }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+
+        titleView = requireViewById(R.id.title)
+        subtitleView = requireViewById(R.id.subtitle)
+        descriptionView = requireViewById(R.id.description)
+        iconView = requireViewById(R.id.icon)
+        subtitleView = requireViewById(R.id.subtitle)
+        passwordField = requireViewById(R.id.lockPassword)
+        credentialHeader = requireViewById(R.id.auth_credential_header)
+        credentialInput = requireViewById(R.id.auth_credential_input)
+
+        setOnApplyWindowInsetsListener(this)
+    }
+
+    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+        super.onLayout(changed, left, top, right, bottom)
+
+        val inputLeftBound: Int
+        val inputTopBound: Int
+        var headerRightBound = right
+        var headerTopBounds = top
+        val subTitleBottom: Int = if (subtitleView.isGone) titleView.bottom else subtitleView.bottom
+        val descBottom = if (descriptionView.isGone) subTitleBottom else descriptionView.bottom
+        if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
+            inputTopBound = (bottom - credentialInput.height) / 2
+            inputLeftBound = (right - left) / 2
+            headerRightBound = inputLeftBound
+            headerTopBounds -= iconView.bottom.coerceAtMost(bottomInset)
+        } else {
+            inputTopBound = descBottom + (bottom - descBottom - credentialInput.height) / 2
+            inputLeftBound = (right - left - credentialInput.width) / 2
+        }
+
+        if (descriptionView.bottom > bottomInset) {
+            credentialHeader.layout(left, headerTopBounds, headerRightBound, bottom)
+        }
+        credentialInput.layout(inputLeftBound, inputTopBound, right, bottom)
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+
+        val newWidth = MeasureSpec.getSize(widthMeasureSpec)
+        val newHeight = MeasureSpec.getSize(heightMeasureSpec) - bottomInset
+
+        setMeasuredDimension(newWidth, newHeight)
+
+        val halfWidthSpec = MeasureSpec.makeMeasureSpec(width / 2, MeasureSpec.AT_MOST)
+        val fullHeightSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.UNSPECIFIED)
+        if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
+            measureChildren(halfWidthSpec, fullHeightSpec)
+        } else {
+            measureChildren(widthMeasureSpec, fullHeightSpec)
+        }
+    }
+
+    override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
+        val bottomInsets = insets.getInsets(ime())
+        if (bottomInset != bottomInsets.bottom) {
+            bottomInset = bottomInsets.bottom
+
+            if (bottomInset > 0 && resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
+                titleView.isSingleLine = true
+                titleView.ellipsize = TextUtils.TruncateAt.MARQUEE
+                titleView.marqueeRepeatLimit = -1
+                // select to enable marquee unless a screen reader is enabled
+                titleView.isSelected = accessibilityManager.shouldMarquee()
+            } else {
+                titleView.isSingleLine = false
+                titleView.ellipsize = null
+                // select to enable marquee unless a screen reader is enabled
+                titleView.isSelected = false
+            }
+
+            requestLayout()
+        }
+        return insets
+    }
+}
+
+private fun AccessibilityManager.shouldMarquee(): Boolean = !isEnabled || !isTouchExplorationEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt
new file mode 100644
index 0000000..75331f0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt
@@ -0,0 +1,23 @@
+package com.android.systemui.biometrics.ui
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+import com.android.systemui.biometrics.AuthPanelController
+import com.android.systemui.biometrics.ui.binder.CredentialViewBinder
+import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
+
+/** Pattern credential view for BiometricPrompt. */
+class CredentialPatternView(context: Context, attrs: AttributeSet?) :
+    LinearLayout(context, attrs), CredentialView {
+
+    /** Initializes the view. */
+    override fun init(
+        viewModel: CredentialViewModel,
+        host: CredentialView.Host,
+        panelViewController: AuthPanelController,
+        animatePanel: Boolean,
+    ) {
+        CredentialViewBinder.bind(this, host, viewModel, panelViewController, animatePanel)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt
new file mode 100644
index 0000000..b7c6a45
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt
@@ -0,0 +1,31 @@
+package com.android.systemui.biometrics.ui
+
+import com.android.systemui.biometrics.AuthPanelController
+import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
+
+/** A credential variant of BiometricPrompt. */
+sealed interface CredentialView {
+    /**
+     * Callbacks for the "host" container view that contains this credential view.
+     *
+     * TODO(b/251476085): Removed when the host view is converted to use a parent view model.
+     */
+    interface Host {
+        /** When the user's credential has been verified. */
+        fun onCredentialMatched(attestation: ByteArray)
+
+        /** When the user abandons credential verification. */
+        fun onCredentialAborted()
+
+        /** Warn the user is warned about excessive attempts. */
+        fun onCredentialAttemptsRemaining(remaining: Int, messageBody: String)
+    }
+
+    // TODO(251476085): remove AuthPanelController
+    fun init(
+        viewModel: CredentialViewModel,
+        host: Host,
+        panelViewController: AuthPanelController,
+        animatePanel: Boolean,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
new file mode 100644
index 0000000..c619648
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
@@ -0,0 +1,104 @@
+package com.android.systemui.biometrics.ui.binder
+
+import android.view.KeyEvent
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputMethodManager
+import android.widget.ImeAwareEditText
+import android.widget.TextView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.biometrics.ui.CredentialPasswordView
+import com.android.systemui.biometrics.ui.CredentialView
+import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+/** Sub-binder for the [CredentialPasswordView]. */
+object CredentialPasswordViewBinder {
+
+    /** Bind the view. */
+    fun bind(
+        view: CredentialPasswordView,
+        host: CredentialView.Host,
+        viewModel: CredentialViewModel,
+    ) {
+        val imeManager = view.context.getSystemService(InputMethodManager::class.java)!!
+
+        val passwordField: ImeAwareEditText = view.requireViewById(R.id.lockPassword)
+
+        view.repeatWhenAttached {
+            passwordField.requestFocus()
+            passwordField.scheduleShowSoftInput()
+
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                // observe credential validation attempts and submit/cancel buttons
+                launch {
+                    viewModel.header.collect { header ->
+                        passwordField.setTextOperationUser(header.user)
+                        passwordField.setOnEditorActionListener(
+                            OnImeSubmitListener { text ->
+                                launch { viewModel.checkCredential(text, header) }
+                            }
+                        )
+                        passwordField.setOnKeyListener(
+                            OnBackButtonListener { host.onCredentialAborted() }
+                        )
+                    }
+                }
+
+                launch {
+                    viewModel.inputFlags.collect { flags ->
+                        flags?.let { passwordField.inputType = it }
+                    }
+                }
+
+                // dismiss on a valid credential check
+                launch {
+                    viewModel.validatedAttestation.collect { attestation ->
+                        if (attestation != null) {
+                            imeManager.hideSoftInputFromWindow(view.windowToken, 0 /* flags */)
+                            host.onCredentialMatched(attestation)
+                        } else {
+                            passwordField.setText("")
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+private class OnBackButtonListener(private val onBack: () -> Unit) : View.OnKeyListener {
+    override fun onKey(v: View, keyCode: Int, event: KeyEvent): Boolean {
+        if (keyCode != KeyEvent.KEYCODE_BACK) {
+            return false
+        }
+        if (event.action == KeyEvent.ACTION_UP) {
+            onBack()
+        }
+        return true
+    }
+}
+
+private class OnImeSubmitListener(private val onSubmit: (text: CharSequence) -> Unit) :
+    TextView.OnEditorActionListener {
+    override fun onEditorAction(v: TextView, actionId: Int, event: KeyEvent?): Boolean {
+        val isSoftImeEvent =
+            event == null &&
+                (actionId == EditorInfo.IME_NULL ||
+                    actionId == EditorInfo.IME_ACTION_DONE ||
+                    actionId == EditorInfo.IME_ACTION_NEXT)
+        val isKeyboardEnterKey =
+            event != null &&
+                KeyEvent.isConfirmKey(event.keyCode) &&
+                event.action == KeyEvent.ACTION_DOWN
+        if (isSoftImeEvent || isKeyboardEnterKey) {
+            onSubmit(v.text)
+            return true
+        }
+        return false
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt
new file mode 100644
index 0000000..4765551
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt
@@ -0,0 +1,75 @@
+package com.android.systemui.biometrics.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternView
+import com.android.systemui.R
+import com.android.systemui.biometrics.ui.CredentialPatternView
+import com.android.systemui.biometrics.ui.CredentialView
+import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+/** Sub-binder for the [CredentialPatternView]. */
+object CredentialPatternViewBinder {
+
+    /** Bind the view. */
+    fun bind(
+        view: CredentialPatternView,
+        host: CredentialView.Host,
+        viewModel: CredentialViewModel,
+    ) {
+        val lockPatternView: LockPatternView = view.requireViewById(R.id.lockPattern)
+
+        view.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                // observe credential validation attempts and submit/cancel buttons
+                launch {
+                    viewModel.header.collect { header ->
+                        lockPatternView.setOnPatternListener(
+                            OnPatternDetectedListener { pattern ->
+                                if (pattern.isPatternLongEnough()) {
+                                    // Pattern size is less than the minimum
+                                    // do not count it as a failed attempt
+                                    viewModel.showPatternTooShortError()
+                                } else {
+                                    lockPatternView.isEnabled = false
+                                    launch { viewModel.checkCredential(pattern, header) }
+                                }
+                            }
+                        )
+                    }
+                }
+
+                launch { viewModel.stealthMode.collect { lockPatternView.isInStealthMode = it } }
+
+                // dismiss on a valid credential check
+                launch {
+                    viewModel.validatedAttestation.collect { attestation ->
+                        val matched = attestation != null
+                        lockPatternView.isEnabled = !matched
+                        if (matched) {
+                            host.onCredentialMatched(attestation!!)
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+private class OnPatternDetectedListener(
+    private val onDetected: (pattern: List<LockPatternView.Cell>) -> Unit
+) : LockPatternView.OnPatternListener {
+    override fun onPatternCellAdded(pattern: List<LockPatternView.Cell>) {}
+    override fun onPatternCleared() {}
+    override fun onPatternStart() {}
+    override fun onPatternDetected(pattern: List<LockPatternView.Cell>) {
+        onDetected(pattern)
+    }
+}
+
+private fun List<LockPatternView.Cell>.isPatternLongEnough(): Boolean =
+    size < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
new file mode 100644
index 0000000..fcc9487
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -0,0 +1,140 @@
+package com.android.systemui.biometrics.ui.binder
+
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.biometrics.AuthDialog
+import com.android.systemui.biometrics.AuthPanelController
+import com.android.systemui.biometrics.ui.CredentialPasswordView
+import com.android.systemui.biometrics.ui.CredentialPatternView
+import com.android.systemui.biometrics.ui.CredentialView
+import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+/**
+ * View binder for all credential variants of BiometricPrompt, including [CredentialPatternView] and
+ * [CredentialPasswordView].
+ *
+ * This binder delegates to sub-binders for each variant, such as the [CredentialPasswordViewBinder]
+ * and [CredentialPatternViewBinder].
+ */
+object CredentialViewBinder {
+
+    /** Binds a [CredentialPasswordView] or [CredentialPatternView] to a [CredentialViewModel]. */
+    @JvmStatic
+    fun bind(
+        view: ViewGroup,
+        host: CredentialView.Host,
+        viewModel: CredentialViewModel,
+        panelViewController: AuthPanelController,
+        animatePanel: Boolean,
+        maxErrorDuration: Long = 3_000L,
+    ) {
+        val titleView: TextView = view.requireViewById(R.id.title)
+        val subtitleView: TextView = view.requireViewById(R.id.subtitle)
+        val descriptionView: TextView = view.requireViewById(R.id.description)
+        val iconView: ImageView? = view.findViewById(R.id.icon)
+        val errorView: TextView = view.requireViewById(R.id.error)
+
+        var errorTimer: Job? = null
+
+        // bind common elements
+        view.repeatWhenAttached {
+            if (animatePanel) {
+                with(panelViewController) {
+                    // Credential view is always full screen.
+                    setUseFullScreen(true)
+                    updateForContentDimensions(
+                        containerWidth,
+                        containerHeight,
+                        0 /* animateDurationMs */
+                    )
+                }
+            }
+
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                // show prompt metadata
+                launch {
+                    viewModel.header.collect { header ->
+                        titleView.text = header.title
+                        view.announceForAccessibility(header.title)
+
+                        subtitleView.textOrHide = header.subtitle
+                        descriptionView.textOrHide = header.description
+
+                        iconView?.setImageDrawable(header.icon)
+
+                        // Only animate this if we're transitioning from a biometric view.
+                        if (viewModel.animateContents.value) {
+                            view.animateCredentialViewIn()
+                        }
+                    }
+                }
+
+                // show transient error messages
+                launch {
+                    viewModel.errorMessage
+                        .onEach { msg ->
+                            errorTimer?.cancel()
+                            if (msg.isNotBlank()) {
+                                errorTimer = launch {
+                                    delay(maxErrorDuration)
+                                    viewModel.resetErrorMessage()
+                                }
+                            }
+                        }
+                        .collect { errorView.textOrHide = it }
+                }
+
+                // show an extra dialog if the remaining attempts becomes low
+                launch {
+                    viewModel.remainingAttempts
+                        .filter { it.remaining != null }
+                        .collect { info ->
+                            host.onCredentialAttemptsRemaining(info.remaining!!, info.message)
+                        }
+                }
+            }
+        }
+
+        // bind the auth widget
+        when (view) {
+            is CredentialPasswordView -> CredentialPasswordViewBinder.bind(view, host, viewModel)
+            is CredentialPatternView -> CredentialPatternViewBinder.bind(view, host, viewModel)
+            else -> throw IllegalStateException("unexpected view type: ${view.javaClass.name}")
+        }
+    }
+}
+
+private fun View.animateCredentialViewIn() {
+    translationY = resources.getDimension(R.dimen.biometric_dialog_credential_translation_offset)
+    alpha = 0f
+    postOnAnimation {
+        animate()
+            .translationY(0f)
+            .setDuration(AuthDialog.ANIMATE_CREDENTIAL_INITIAL_DURATION_MS.toLong())
+            .alpha(1f)
+            .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+            .withLayer()
+            .start()
+    }
+}
+
+private var TextView.textOrHide: String?
+    set(value) {
+        val gone = value.isNullOrBlank()
+        visibility = if (gone) View.GONE else View.VISIBLE
+        text = if (gone) "" else value
+    }
+    get() = text?.toString()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
new file mode 100644
index 0000000..84bbceb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -0,0 +1,178 @@
+package com.android.systemui.biometrics.ui.viewmodel
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.text.InputType
+import com.android.internal.widget.LockPatternView
+import com.android.systemui.R
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor
+import com.android.systemui.biometrics.domain.interactor.CredentialStatus
+import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlin.reflect.KClass
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.map
+
+/** View-model for all CredentialViews within BiometricPrompt. */
+class CredentialViewModel
+@Inject
+constructor(
+    @Application private val applicationContext: Context,
+    private val credentialInteractor: BiometricPromptCredentialInteractor,
+) {
+
+    /** Top level information about the prompt. */
+    val header: Flow<HeaderViewModel> =
+        credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>().map {
+            request ->
+            BiometricPromptHeaderViewModelImpl(
+                request,
+                user = UserHandle.of(request.userInfo.userId),
+                title = request.title,
+                subtitle = request.subtitle,
+                description = request.description,
+                icon = applicationContext.asLockIcon(request.userInfo.deviceCredentialOwnerId),
+            )
+        }
+
+    /** Input flags for text based credential views */
+    val inputFlags: Flow<Int?> =
+        credentialInteractor.prompt.map {
+            when (it) {
+                is BiometricPromptRequest.Credential.Pin ->
+                    InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
+                else -> null
+            }
+        }
+
+    /** If stealth mode is active (hide user credential input). */
+    val stealthMode: Flow<Boolean> =
+        credentialInteractor.prompt.map {
+            when (it) {
+                is BiometricPromptRequest.Credential.Pattern -> it.stealthMode
+                else -> false
+            }
+        }
+
+    private val _animateContents: MutableStateFlow<Boolean> = MutableStateFlow(true)
+    /** If this view should be animated on transitions. */
+    val animateContents = _animateContents.asStateFlow()
+
+    /** Error messages to show the user. */
+    val errorMessage: Flow<String> =
+        combine(credentialInteractor.verificationError, credentialInteractor.prompt) { error, p ->
+            when (error) {
+                is CredentialStatus.Fail.Error -> error.error
+                        ?: applicationContext.asBadCredentialErrorMessage(p)
+                is CredentialStatus.Fail.Throttled -> error.error
+                null -> ""
+            }
+        }
+
+    private val _validatedAttestation: MutableSharedFlow<ByteArray?> = MutableSharedFlow()
+    /** Results of [checkPatternCredential]. A non-null attestation is supplied on success. */
+    val validatedAttestation: Flow<ByteArray?> = _validatedAttestation.asSharedFlow()
+
+    private val _remainingAttempts: MutableStateFlow<RemainingAttempts> =
+        MutableStateFlow(RemainingAttempts())
+    /** If set, the number of remaining attempts before the user must stop. */
+    val remainingAttempts: Flow<RemainingAttempts> = _remainingAttempts.asStateFlow()
+
+    /** Enable transition animations. */
+    fun setAnimateContents(animate: Boolean) {
+        _animateContents.value = animate
+    }
+
+    /** Show an error message to inform the user the pattern is too short to attempt validation. */
+    fun showPatternTooShortError() {
+        credentialInteractor.setVerificationError(
+            CredentialStatus.Fail.Error(
+                applicationContext.asBadCredentialErrorMessage(
+                    BiometricPromptRequest.Credential.Pattern::class
+                )
+            )
+        )
+    }
+
+    /** Reset the error message to an empty string. */
+    fun resetErrorMessage() {
+        credentialInteractor.resetVerificationError()
+    }
+
+    /** Check a PIN or password and update [validatedAttestation] or [remainingAttempts]. */
+    suspend fun checkCredential(text: CharSequence, header: HeaderViewModel) =
+        checkCredential(credentialInteractor.checkCredential(header.asRequest(), text = text))
+
+    /** Check a pattern and update [validatedAttestation] or [remainingAttempts]. */
+    suspend fun checkCredential(pattern: List<LockPatternView.Cell>, header: HeaderViewModel) =
+        checkCredential(credentialInteractor.checkCredential(header.asRequest(), pattern = pattern))
+
+    private suspend fun checkCredential(result: CredentialStatus) {
+        when (result) {
+            is CredentialStatus.Success.Verified -> {
+                _validatedAttestation.emit(result.hat)
+                _remainingAttempts.value = RemainingAttempts()
+            }
+            is CredentialStatus.Fail.Error -> {
+                _validatedAttestation.emit(null)
+                _remainingAttempts.value =
+                    RemainingAttempts(result.remainingAttempts, result.urgentMessage ?: "")
+            }
+            is CredentialStatus.Fail.Throttled -> {
+                // required for completeness, but a throttled error cannot be the final result
+                _validatedAttestation.emit(null)
+                _remainingAttempts.value = RemainingAttempts()
+            }
+        }
+    }
+}
+
+private fun Context.asBadCredentialErrorMessage(prompt: BiometricPromptRequest?): String =
+    asBadCredentialErrorMessage(
+        if (prompt != null) prompt::class else BiometricPromptRequest.Credential.Password::class
+    )
+
+private fun <T : BiometricPromptRequest> Context.asBadCredentialErrorMessage(
+    clazz: KClass<T>
+): String =
+    getString(
+        when (clazz) {
+            BiometricPromptRequest.Credential.Pin::class -> R.string.biometric_dialog_wrong_pin
+            BiometricPromptRequest.Credential.Password::class ->
+                R.string.biometric_dialog_wrong_password
+            BiometricPromptRequest.Credential.Pattern::class ->
+                R.string.biometric_dialog_wrong_pattern
+            else -> R.string.biometric_dialog_wrong_password
+        }
+    )
+
+private fun Context.asLockIcon(userId: Int): Drawable {
+    val id =
+        if (Utils.isManagedProfile(this, userId)) {
+            R.drawable.auth_dialog_enterprise
+        } else {
+            R.drawable.auth_dialog_lock
+        }
+    return resources.getDrawable(id, theme)
+}
+
+private class BiometricPromptHeaderViewModelImpl(
+    val request: BiometricPromptRequest.Credential,
+    override val user: UserHandle,
+    override val title: String,
+    override val subtitle: String,
+    override val description: String,
+    override val icon: Drawable,
+) : HeaderViewModel
+
+private fun HeaderViewModel.asRequest(): BiometricPromptRequest.Credential =
+    (this as BiometricPromptHeaderViewModelImpl).request
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/HeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/HeaderViewModel.kt
new file mode 100644
index 0000000..ba23f1c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/HeaderViewModel.kt
@@ -0,0 +1,13 @@
+package com.android.systemui.biometrics.ui.viewmodel
+
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+
+/** View model for the top-level header / info area of BiometricPrompt. */
+interface HeaderViewModel {
+    val user: UserHandle
+    val title: String
+    val subtitle: String
+    val description: String
+    val icon: Drawable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/RemainingAttempts.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/RemainingAttempts.kt
new file mode 100644
index 0000000..0f22173
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/RemainingAttempts.kt
@@ -0,0 +1,4 @@
+package com.android.systemui.biometrics.ui.viewmodel
+
+/** Metadata about the number of credential attempts the user has left [remaining], if known. */
+data class RemainingAttempts(val remaining: Int? = null, val message: String = "")
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index dec3d6b..616e49c 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -149,7 +149,7 @@
     }
 
     fun startRipple() {
-        if (rippleView.rippleInProgress || rippleView.parent != null) {
+        if (rippleView.rippleInProgress() || rippleView.parent != null) {
             // Skip if ripple is still playing, or not playing but already added the parent
             // (which might happen just before the animation starts or right after
             // the animation ends.)
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index c0cc6b4..1455699 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -33,9 +33,9 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.ripple.RippleAnimationConfig;
 import com.android.systemui.ripple.RippleShader.RippleShape;
 import com.android.systemui.ripple.RippleView;
-import com.android.systemui.ripple.RippleViewKt;
 
 import java.text.NumberFormat;
 
@@ -150,7 +150,7 @@
             mRippleView.setColor(color, 28);
         } else {
             mRippleView.setDuration(CIRCLE_RIPPLE_ANIMATION_DURATION);
-            mRippleView.setColor(color, RippleViewKt.RIPPLE_DEFAULT_ALPHA);
+            mRippleView.setColor(color, RippleAnimationConfig.RIPPLE_DEFAULT_ALPHA);
         }
 
         OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 9f338d1..c853671 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -31,6 +31,7 @@
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT;
+import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR;
 
 import static java.util.Objects.requireNonNull;
 
@@ -73,6 +74,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.screenshot.TimeoutHandler;
 
 import java.io.IOException;
@@ -101,6 +103,8 @@
     private final ClipboardOverlayWindow mWindow;
     private final TimeoutHandler mTimeoutHandler;
     private final TextClassifier mTextClassifier;
+    private final ClipboardOverlayUtils mClipboardUtils;
+    private final FeatureFlags mFeatureFlags;
 
     private final ClipboardOverlayView mView;
 
@@ -119,11 +123,15 @@
     private Animator mExitAnimator;
     private Animator mEnterAnimator;
 
+    private Runnable mOnUiUpdate;
+
     private final ClipboardOverlayView.ClipboardOverlayCallbacks mClipboardCallbacks =
             new ClipboardOverlayView.ClipboardOverlayCallbacks() {
                 @Override
                 public void onInteraction() {
-                    mTimeoutHandler.resetTimeout();
+                    if (mOnUiUpdate != null) {
+                        mOnUiUpdate.run();
+                    }
                 }
 
                 @Override
@@ -178,7 +186,10 @@
             ClipboardOverlayWindow clipboardOverlayWindow,
             BroadcastDispatcher broadcastDispatcher,
             BroadcastSender broadcastSender,
-            TimeoutHandler timeoutHandler, UiEventLogger uiEventLogger) {
+            TimeoutHandler timeoutHandler,
+            FeatureFlags featureFlags,
+            ClipboardOverlayUtils clipboardUtils,
+            UiEventLogger uiEventLogger) {
         mBroadcastDispatcher = broadcastDispatcher;
         mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
         final Context displayContext = context.createDisplayContext(getDefaultDisplay());
@@ -199,6 +210,9 @@
         mTimeoutHandler = timeoutHandler;
         mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS);
 
+        mFeatureFlags = featureFlags;
+        mClipboardUtils = clipboardUtils;
+
         mView.setCallbacks(mClipboardCallbacks);
 
 
@@ -257,11 +271,13 @@
         boolean isSensitive = clipData != null && clipData.getDescription().getExtras() != null
                 && clipData.getDescription().getExtras()
                 .getBoolean(ClipDescription.EXTRA_IS_SENSITIVE);
+        boolean isRemote = mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR)
+                && mClipboardUtils.isRemoteCopy(mContext, clipData, clipSource);
         if (clipData == null || clipData.getItemCount() == 0) {
             mView.showDefaultTextPreview();
         } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
             ClipData.Item item = clipData.getItemAt(0);
-            if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+            if (isRemote || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
                     CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) {
                 if (item.getTextLinks() != null) {
                     AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource));
@@ -287,7 +303,13 @@
         maybeShowRemoteCopy(clipData);
         animateIn();
         mView.announceForAccessibility(accessibilityAnnouncement);
-        mTimeoutHandler.resetTimeout();
+        if (isRemote) {
+            mTimeoutHandler.cancelTimeout();
+            mOnUiUpdate = null;
+        } else {
+            mOnUiUpdate = mTimeoutHandler::resetTimeout;
+            mOnUiUpdate.run();
+        }
     }
 
     private void maybeShowRemoteCopy(ClipData clipData) {
@@ -427,7 +449,9 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
-                mTimeoutHandler.resetTimeout();
+                if (mOnUiUpdate != null) {
+                    mOnUiUpdate.run();
+                }
             }
         });
         mEnterAnimator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
new file mode 100644
index 0000000..cece764
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
@@ -0,0 +1,46 @@
+/*
+ * 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.clipboardoverlay;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.systemui.R;
+
+import javax.inject.Inject;
+
+class ClipboardOverlayUtils {
+
+    @Inject
+    ClipboardOverlayUtils() {
+    }
+
+    boolean isRemoteCopy(Context context, ClipData clipData, String clipSource) {
+        if (clipData != null && clipData.getDescription().getExtras() != null
+                && clipData.getDescription().getExtras().getBoolean(
+                ClipDescription.EXTRA_IS_REMOTE_DEVICE)) {
+            ComponentName remoteComponent = ComponentName.unflattenFromString(
+                    context.getResources().getString(R.string.config_remoteCopyPackage));
+            if (remoteComponent != null) {
+                return remoteComponent.getPackageName().equals(clipSource);
+            }
+        }
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index e47e636..6db56210 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -84,6 +84,7 @@
 import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
 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.tuner.dagger.TunerModule;
 import com.android.systemui.unfold.SysUIUnfoldModule;
 import com.android.systemui.user.UserModule;
@@ -150,6 +151,7 @@
             SysUIConcurrencyModule.class,
             SysUIUnfoldModule.class,
             TelephonyRepositoryModule.class,
+            TemporaryDisplayModule.class,
             TunerModule.class,
             UserModule.class,
             UtilModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index 3adeeac..1f061e9 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -21,6 +21,7 @@
 import static com.android.systemui.flags.FlagManager.EXTRA_FLAGS;
 import static com.android.systemui.flags.FlagManager.EXTRA_ID;
 import static com.android.systemui.flags.FlagManager.EXTRA_VALUE;
+import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS;
 
 import static java.util.Objects.requireNonNull;
 
@@ -59,20 +60,20 @@
  *
  * Flags can be set (or unset) via the following adb command:
  *
- *   adb shell cmd statusbar flag <id> <on|off|toggle|erase>
+ * adb shell cmd statusbar flag <id> <on|off|toggle|erase>
  *
- *  Alternatively, you can change flags via a broadcast intent:
+ * Alternatively, you can change flags via a broadcast intent:
  *
- *   adb shell am broadcast -a com.android.systemui.action.SET_FLAG --ei id <id> [--ez value <0|1>]
+ * adb shell am broadcast -a com.android.systemui.action.SET_FLAG --ei id <id> [--ez value <0|1>]
  *
  * To restore a flag back to its default, leave the `--ez value <0|1>` off of the command.
  */
 @SysUISingleton
 public class FeatureFlagsDebug implements FeatureFlags {
     static final String TAG = "SysUIFlags";
-    static final String ALL_FLAGS = "all_flags";
 
     private final FlagManager mFlagManager;
+    private final Context mContext;
     private final SecureSettings mSecureSettings;
     private final Resources mResources;
     private final SystemPropertiesHelper mSystemProperties;
@@ -83,6 +84,14 @@
     private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
     private final Restarter mRestarter;
 
+    private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
+            new ServerFlagReader.ChangeListener() {
+                @Override
+                public void onChange() {
+                    mRestarter.restart();
+                }
+            };
+
     @Inject
     public FeatureFlagsDebug(
             FlagManager flagManager,
@@ -93,23 +102,28 @@
             DeviceConfigProxy deviceConfigProxy,
             ServerFlagReader serverFlagReader,
             @Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
-            Restarter barService) {
+            Restarter restarter) {
         mFlagManager = flagManager;
+        mContext = context;
         mSecureSettings = secureSettings;
         mResources = resources;
         mSystemProperties = systemProperties;
         mDeviceConfigProxy = deviceConfigProxy;
         mServerFlagReader = serverFlagReader;
         mAllFlags = allFlags;
-        mRestarter = barService;
+        mRestarter = restarter;
+    }
 
+    /** Call after construction to setup listeners. */
+    void init() {
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_SET_FLAG);
         filter.addAction(ACTION_GET_FLAGS);
-        flagManager.setOnSettingsChangedAction(this::restartSystemUI);
-        flagManager.setClearCacheAction(this::removeFromCache);
-        context.registerReceiver(mReceiver, filter, null, null,
+        mFlagManager.setOnSettingsChangedAction(this::restartSystemUI);
+        mFlagManager.setClearCacheAction(this::removeFromCache);
+        mContext.registerReceiver(mReceiver, filter, null, null,
                 Context.RECEIVER_EXPORTED_UNAUDITED);
+        mServerFlagReader.listenForChanges(mAllFlags.values(), mOnPropertiesChanged);
     }
 
     @Override
@@ -196,7 +210,7 @@
         return mStringFlagCache.get(id);
     }
 
-    /** Specific override for Boolean flags that checks against the teamfood list.*/
+    /** Specific override for Boolean flags that checks against the teamfood list. */
     private boolean readFlagValue(int id, boolean defaultValue) {
         Boolean result = readBooleanFlagOverride(id);
         boolean hasServerOverride = mServerFlagReader.hasOverride(id);
@@ -273,6 +287,7 @@
     private void dispatchListenersAndMaybeRestart(int id, Consumer<Boolean> restartAction) {
         mFlagManager.dispatchListenersAndMaybeRestart(id, restartAction);
     }
+
     /** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */
     private void eraseInternal(int id) {
         // We can't actually "erase" things from sysprops, but we can set them to empty!
@@ -358,7 +373,7 @@
                     }
                 }
 
-                Bundle extras =  getResultExtras(true);
+                Bundle extras = getResultExtras(true);
                 if (extras != null) {
                     extras.putParcelableArrayList(EXTRA_FLAGS, pFlags);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
index 560dcbd..6271334 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
@@ -31,7 +31,7 @@
     dumpManager: DumpManager,
     private val commandRegistry: CommandRegistry,
     private val flagCommand: FlagCommand,
-    featureFlags: FeatureFlags
+    private val featureFlags: FeatureFlagsDebug
 ) : CoreStartable {
 
     init {
@@ -41,6 +41,7 @@
     }
 
     override fun start() {
+        featureFlags.init()
         commandRegistry.registerCommand(FlagCommand.FLAG_COMMAND) { flagCommand }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 40a8a1a..30cad5f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.flags;
 
+import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS;
+
 import static java.util.Objects.requireNonNull;
 
 import android.content.res.Resources;
@@ -34,6 +36,7 @@
 import java.util.Map;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 /**
  * Default implementation of the a Flag manager that returns default values for release builds
@@ -49,26 +52,47 @@
     private final SystemPropertiesHelper mSystemProperties;
     private final DeviceConfigProxy mDeviceConfigProxy;
     private final ServerFlagReader mServerFlagReader;
+    private final Restarter mRestarter;
+    private final Map<Integer, Flag<?>> mAllFlags;
     SparseBooleanArray mBooleanCache = new SparseBooleanArray();
     SparseArray<String> mStringCache = new SparseArray<>();
 
+    private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
+            new ServerFlagReader.ChangeListener() {
+                @Override
+                public void onChange() {
+                    mRestarter.restart();
+                }
+            };
+
     @Inject
     public FeatureFlagsRelease(
             @Main Resources resources,
             SystemPropertiesHelper systemProperties,
             DeviceConfigProxy deviceConfigProxy,
-            ServerFlagReader serverFlagReader) {
+            ServerFlagReader serverFlagReader,
+            @Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
+            Restarter restarter) {
         mResources = resources;
         mSystemProperties = systemProperties;
         mDeviceConfigProxy = deviceConfigProxy;
         mServerFlagReader = serverFlagReader;
+        mAllFlags = allFlags;
+        mRestarter = restarter;
+    }
+
+    /** Call after construction to setup listeners. */
+    void init() {
+        mServerFlagReader.listenForChanges(mAllFlags.values(), mOnPropertiesChanged);
     }
 
     @Override
-    public void addListener(@NonNull Flag<?> flag, @NonNull Listener listener) {}
+    public void addListener(@NonNull Flag<?> flag, @NonNull Listener listener) {
+    }
 
     @Override
-    public void removeListener(@NonNull Listener listener) {}
+    public void removeListener(@NonNull Listener listener) {
+    }
 
     @Override
     public boolean isEnabled(@NotNull UnreleasedFlag flag) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
index 4d25431..1e93c0b7 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.flags;
 
+import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS;
+
 import androidx.annotation.NonNull;
 
 import com.android.systemui.statusbar.commandline.Command;
@@ -42,7 +44,7 @@
     @Inject
     FlagCommand(
             FeatureFlagsDebug featureFlags,
-            @Named(FeatureFlagsDebug.ALL_FLAGS) Map<Integer, Flag<?>> allFlags
+            @Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags
     ) {
         mFeatureFlags = featureFlags;
         mAllFlags = allFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 4818bcc..7b1ba0d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -63,7 +63,7 @@
     @JvmField val NOTIFICATION_DISMISSAL_FADE = UnreleasedFlag(113, teamfood = true)
     val STABILITY_INDEX_FIX = UnreleasedFlag(114, teamfood = true)
     val SEMI_STABLE_SORT = UnreleasedFlag(115, teamfood = true)
-    @JvmField val NOTIFICATION_GROUP_CORNER = UnreleasedFlag(116, true)
+    @JvmField val NOTIFICATION_GROUP_CORNER = UnreleasedFlag(116, teamfood = true)
     // next id: 117
 
     // 200 - keyguard/lockscreen
@@ -122,7 +122,12 @@
      * Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
      * will occur in stages. This is one stage of many to come.
      */
-    @JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213, teamfood = true)
+    // TODO(b/255607168): Tracking Bug
+    @JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213)
+
+    @JvmField val NEW_ELLIPSE_DETECTION = UnreleasedFlag(214)
+
+    @JvmField val NEW_UDFPS_OVERLAY = UnreleasedFlag(215)
 
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
@@ -173,9 +178,9 @@
     @Deprecated("Replaced by mobile and wifi specific flags.")
     val NEW_STATUS_BAR_PIPELINE_FRONTEND = UnreleasedFlag(605, teamfood = false)
 
-    val NEW_STATUS_BAR_MOBILE_ICONS = UnreleasedFlag(606, false)
+    val NEW_STATUS_BAR_MOBILE_ICONS = UnreleasedFlag(606)
 
-    val NEW_STATUS_BAR_WIFI_ICON = UnreleasedFlag(607, false)
+    val NEW_STATUS_BAR_WIFI_ICON = UnreleasedFlag(607)
 
     // 700 - dialer/calls
     // TODO(b/254512734): Tracking Bug
@@ -196,7 +201,7 @@
 
     // 802 - wallpaper rendering
     // TODO(b/254512923): Tracking Bug
-    @JvmField val USE_CANVAS_RENDERER = ReleasedFlag(802)
+    @JvmField val USE_CANVAS_RENDERER = UnreleasedFlag(802, teamfood = true)
 
     // 803 - screen contents translation
     // TODO(b/254513187): Tracking Bug
@@ -225,20 +230,14 @@
     @JvmField val DREAM_MEDIA_TAP_TO_OPEN = UnreleasedFlag(906)
 
     // TODO(b/254513168): Tracking Bug
-    val UMO_SURFACE_RIPPLE = UnreleasedFlag(907)
+    @JvmField val UMO_SURFACE_RIPPLE = UnreleasedFlag(907)
 
     // 1000 - dock
     val SIMULATE_DOCK_THROUGH_CHARGING = ReleasedFlag(1000)
 
-    // TODO(b/254512444): Tracking Bug
-    @JvmField val DOCK_SETUP_ENABLED = ReleasedFlag(1001)
-
     // TODO(b/254512758): Tracking Bug
     @JvmField val ROUNDED_BOX_RIPPLE = ReleasedFlag(1002)
 
-    // TODO(b/254512525): Tracking Bug
-    @JvmField val REFACTORED_DOCK_SETUP = ReleasedFlag(1003, teamfood = true)
-
     // 1100 - windowing
     @Keep
     val WM_ENABLE_SHELL_TRANSITIONS =
@@ -320,16 +319,17 @@
 
     // 1500 - chooser
     // TODO(b/254512507): Tracking Bug
-    val CHOOSER_UNBUNDLED = UnreleasedFlag(1500)
+    val CHOOSER_UNBUNDLED = UnreleasedFlag(1500, teamfood = true)
 
     // 1600 - accessibility
     @JvmField val A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS = UnreleasedFlag(1600)
 
     // 1700 - clipboard
     @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700)
+    @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = UnreleasedFlag(1701)
 
     // 1800 - shade container
-    @JvmField val LEAVE_SHADE_OPEN_FOR_BUGREPORT = UnreleasedFlag(1800, true)
+    @JvmField val LEAVE_SHADE_OPEN_FOR_BUGREPORT = UnreleasedFlag(1800, teamfood = true)
 
     // 1900 - note task
     @JvmField val NOTE_TASKS = SysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
new file mode 100644
index 0000000..e1f4944
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.flags
+
+import com.android.internal.statusbar.IStatusBarService
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+
+/** Module containing shared code for all FeatureFlag implementations. */
+@Module
+interface FlagsCommonModule {
+    companion object {
+        const val ALL_FLAGS = "all_flags"
+
+        @JvmStatic
+        @Provides
+        @Named(ALL_FLAGS)
+        fun providesAllFlags(): Map<Int, Flag<*>> {
+            return Flags.collectFlags()
+        }
+
+        @JvmStatic
+        @Provides
+        fun providesRestarter(barService: IStatusBarService): Restarter {
+            return object : Restarter {
+                override fun restart() {
+                    barService.restart()
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
index fc5b9f4..694fa01 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
@@ -16,9 +16,13 @@
 
 package com.android.systemui.flags
 
+import android.provider.DeviceConfig
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.util.DeviceConfigProxy
-import dagger.Binds
 import dagger.Module
+import dagger.Provides
+import java.util.concurrent.Executor
 import javax.inject.Inject
 
 interface ServerFlagReader {
@@ -27,40 +31,99 @@
 
     /** Returns any stored server-side setting or the default if not set. */
     fun readServerOverride(flagId: Int, default: Boolean): Boolean
+
+    /** Register a listener for changes to any of the passed in flags. */
+    fun listenForChanges(values: Collection<Flag<*>>, listener: ChangeListener)
+
+    interface ChangeListener {
+        fun onChange()
+    }
 }
 
 class ServerFlagReaderImpl @Inject constructor(
-    private val deviceConfig: DeviceConfigProxy
+    private val namespace: String,
+    private val deviceConfig: DeviceConfigProxy,
+    @Background private val executor: Executor
 ) : ServerFlagReader {
+
+    private val listeners =
+        mutableListOf<Pair<ServerFlagReader.ChangeListener, Collection<Flag<*>>>>()
+
+    private val onPropertiesChangedListener = object : DeviceConfig.OnPropertiesChangedListener {
+        override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
+            if (properties.namespace != namespace) {
+                return
+            }
+
+            for ((listener, flags) in listeners) {
+                propLoop@ for (propName in properties.keyset) {
+                    for (flag in flags) {
+                        if (propName == getServerOverrideName(flag.id)) {
+                            listener.onChange()
+                            break@propLoop
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     override fun hasOverride(flagId: Int): Boolean =
         deviceConfig.getProperty(
-            SYSUI_NAMESPACE,
+            namespace,
             getServerOverrideName(flagId)
         ) != null
 
     override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
         return deviceConfig.getBoolean(
-            SYSUI_NAMESPACE,
+            namespace,
             getServerOverrideName(flagId),
             default
         )
     }
 
+    override fun listenForChanges(
+        flags: Collection<Flag<*>>,
+        listener: ServerFlagReader.ChangeListener
+    ) {
+        if (listeners.isEmpty()) {
+            deviceConfig.addOnPropertiesChangedListener(
+                namespace,
+                executor,
+                onPropertiesChangedListener
+            )
+        }
+        listeners.add(Pair(listener, flags))
+    }
+
     private fun getServerOverrideName(flagId: Int): String {
         return "flag_override_$flagId"
     }
 }
 
-private val SYSUI_NAMESPACE = "systemui"
-
 @Module
 interface ServerFlagReaderModule {
-    @Binds
-    fun bindsReader(impl: ServerFlagReaderImpl): ServerFlagReader
+    companion object {
+        private val SYSUI_NAMESPACE = "systemui"
+
+        @JvmStatic
+        @Provides
+        @SysUISingleton
+        fun bindsReader(
+            deviceConfig: DeviceConfigProxy,
+            @Background executor: Executor
+        ): ServerFlagReader {
+            return ServerFlagReaderImpl(
+                SYSUI_NAMESPACE, deviceConfig, executor
+            )
+        }
+    }
 }
 
 class ServerFlagReaderFake : ServerFlagReader {
     private val flagMap: MutableMap<Int, Boolean> = mutableMapOf()
+    private val listeners =
+        mutableListOf<Pair<ServerFlagReader.ChangeListener, Collection<Flag<*>>>>()
 
     override fun hasOverride(flagId: Int): Boolean {
         return flagMap.containsKey(flagId)
@@ -72,9 +135,24 @@
 
     fun setFlagValue(flagId: Int, value: Boolean) {
         flagMap.put(flagId, value)
+
+        for ((listener, flags) in listeners) {
+            flagLoop@ for (flag in flags) {
+                if (flagId == flag.id) {
+                    listener.onChange()
+                    break@flagLoop
+                }
+            }
+        }
     }
 
     fun eraseFlag(flagId: Int) {
         flagMap.remove(flagId)
     }
+
+    override fun listenForChanges(
+        flags: Collection<Flag<*>>,
+        listener: ServerFlagReader.ChangeListener
+    ) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
new file mode 100644
index 0000000..a069582
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.keyguard.data.quickaffordance
+
+/**
+ * Unique identifier keys for all known built-in quick affordances.
+ *
+ * Please ensure uniqueness by never associating more than one class with each key.
+ */
+object BuiltInKeyguardQuickAffordanceKeys {
+    // Please keep alphabetical order of const names to simplify future maintenance.
+    const val HOME_CONTROLS = "home"
+    const val QR_CODE_SCANNER = "qr_code_scanner"
+    const val QUICK_ACCESS_WALLET = "wallet"
+    // Please keep alphabetical order of const names to simplify future maintenance.
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 8384260..c600e13 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -1,21 +1,21 @@
 /*
- *  Copyright (C) 2022 The Android Open Source Project
+ * 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *       http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  *
  */
 
-package com.android.systemui.keyguard.domain.quickaffordance
+package com.android.systemui.keyguard.data.quickaffordance
 
 import android.content.Context
 import android.content.Intent
@@ -51,19 +51,21 @@
 
     private val appContext = context.applicationContext
 
-    override val state: Flow<KeyguardQuickAffordanceConfig.State> =
+    override val key: String = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
+
+    override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
         component.canShowWhileLockedSetting.flatMapLatest { canShowWhileLocked ->
             if (canShowWhileLocked) {
                 stateInternal(component.getControlsListingController().getOrNull())
             } else {
-                flowOf(KeyguardQuickAffordanceConfig.State.Hidden)
+                flowOf(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
             }
         }
 
-    override fun onQuickAffordanceClicked(
+    override fun onTriggered(
         expandable: Expandable?,
-    ): KeyguardQuickAffordanceConfig.OnClickedResult {
-        return KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity(
+    ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+        return KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
             intent =
                 Intent(appContext, ControlsActivity::class.java)
                     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -77,9 +79,9 @@
 
     private fun stateInternal(
         listingController: ControlsListingController?,
-    ): Flow<KeyguardQuickAffordanceConfig.State> {
+    ): Flow<KeyguardQuickAffordanceConfig.LockScreenState> {
         if (listingController == null) {
-            return flowOf(KeyguardQuickAffordanceConfig.State.Hidden)
+            return flowOf(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
         }
 
         return conflatedCallbackFlow {
@@ -114,7 +116,7 @@
         hasServiceInfos: Boolean,
         visibility: ControlsComponent.Visibility,
         @DrawableRes iconResourceId: Int?,
-    ): KeyguardQuickAffordanceConfig.State {
+    ): KeyguardQuickAffordanceConfig.LockScreenState {
         return if (
             isFeatureEnabled &&
                 hasFavorites &&
@@ -122,7 +124,7 @@
                 iconResourceId != null &&
                 visibility == ControlsComponent.Visibility.AVAILABLE
         ) {
-            KeyguardQuickAffordanceConfig.State.Visible(
+            KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                 icon =
                     Icon.Resource(
                         res = iconResourceId,
@@ -133,7 +135,7 @@
                     ),
             )
         } else {
-            KeyguardQuickAffordanceConfig.State.Hidden
+            KeyguardQuickAffordanceConfig.LockScreenState.Hidden
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
new file mode 100644
index 0000000..0a8090b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.keyguard.data.quickaffordance
+
+import android.content.Intent
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import kotlinx.coroutines.flow.Flow
+
+/** Defines interface that can act as data source for a single quick affordance model. */
+interface KeyguardQuickAffordanceConfig {
+
+    /** Unique identifier for this quick affordance. It must be globally unique. */
+    val key: String
+
+    /**
+     * The ever-changing state of the affordance.
+     *
+     * Used to populate the lock screen.
+     */
+    val lockScreenState: Flow<LockScreenState>
+
+    /**
+     * Notifies that the affordance was clicked by the user.
+     *
+     * @param expandable An [Expandable] to use when animating dialogs or activities
+     * @return An [OnTriggeredResult] telling the caller what to do next
+     */
+    fun onTriggered(expandable: Expandable?): OnTriggeredResult
+
+    /**
+     * Encapsulates the state of a "quick affordance" in the keyguard bottom area (for example, a
+     * button on the lock-screen).
+     */
+    sealed class LockScreenState {
+
+        /** No affordance should show up. */
+        object Hidden : LockScreenState()
+
+        /** An affordance is visible. */
+        data class Visible(
+            /** An icon for the affordance. */
+            val icon: Icon,
+            /** The activation state of the affordance. */
+            val activationState: ActivationState = ActivationState.NotSupported,
+        ) : LockScreenState()
+    }
+
+    sealed class OnTriggeredResult {
+        /**
+         * Returning this as a result from the [onTriggered] method means that the implementation
+         * has taken care of the action, the system will do nothing.
+         */
+        object Handled : OnTriggeredResult()
+
+        /**
+         * Returning this as a result from the [onTriggered] method means that the implementation
+         * has _not_ taken care of the action and the system should start an activity using the
+         * given [Intent].
+         */
+        data class StartActivity(
+            val intent: Intent,
+            val canShowWhileLocked: Boolean,
+        ) : OnTriggeredResult()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
new file mode 100644
index 0000000..d620b2a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.keyguard.data.quickaffordance
+
+import com.android.systemui.R
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** QR code scanner quick affordance data source. */
+@SysUISingleton
+class QrCodeScannerKeyguardQuickAffordanceConfig
+@Inject
+constructor(
+    private val controller: QRCodeScannerController,
+) : KeyguardQuickAffordanceConfig {
+
+    override val key: String = BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER
+
+    override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
+        conflatedCallbackFlow {
+            val callback =
+                object : QRCodeScannerController.Callback {
+                    override fun onQRCodeScannerActivityChanged() {
+                        trySendWithFailureLogging(state(), TAG)
+                    }
+                    override fun onQRCodeScannerPreferenceChanged() {
+                        trySendWithFailureLogging(state(), TAG)
+                    }
+                }
+
+            controller.addCallback(callback)
+            controller.registerQRCodeScannerChangeObservers(
+                QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
+                QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE
+            )
+            // Registering does not push an initial update.
+            trySendWithFailureLogging(state(), "initial state", TAG)
+
+            awaitClose {
+                controller.unregisterQRCodeScannerChangeObservers(
+                    QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
+                    QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE
+                )
+                controller.removeCallback(callback)
+            }
+        }
+
+    override fun onTriggered(
+        expandable: Expandable?,
+    ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+        return KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
+            intent = controller.intent,
+            canShowWhileLocked = true,
+        )
+    }
+
+    private fun state(): KeyguardQuickAffordanceConfig.LockScreenState {
+        return if (controller.isEnabledForLockScreenButton) {
+            KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                icon =
+                    Icon.Resource(
+                        res = R.drawable.ic_qr_code_scanner,
+                        contentDescription =
+                            ContentDescription.Resource(
+                                res = R.string.accessibility_qr_code_scanner_button,
+                            ),
+                    ),
+            )
+        } else {
+            KeyguardQuickAffordanceConfig.LockScreenState.Hidden
+        }
+    }
+
+    companion object {
+        private const val TAG = "QrCodeScannerKeyguardQuickAffordanceConfig"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
new file mode 100644
index 0000000..be57a32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.graphics.drawable.Drawable
+import android.service.quickaccesswallet.GetWalletCardsError
+import android.service.quickaccesswallet.GetWalletCardsResponse
+import android.service.quickaccesswallet.QuickAccessWalletClient
+import android.util.Log
+import com.android.systemui.R
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.wallet.controller.QuickAccessWalletController
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Quick access wallet quick affordance data source. */
+@SysUISingleton
+class QuickAccessWalletKeyguardQuickAffordanceConfig
+@Inject
+constructor(
+    private val walletController: QuickAccessWalletController,
+    private val activityStarter: ActivityStarter,
+) : KeyguardQuickAffordanceConfig {
+
+    override val key: String = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
+
+    override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
+        conflatedCallbackFlow {
+            val callback =
+                object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
+                    override fun onWalletCardsRetrieved(response: GetWalletCardsResponse?) {
+                        trySendWithFailureLogging(
+                            state(
+                                isFeatureEnabled = walletController.isWalletEnabled,
+                                hasCard = response?.walletCards?.isNotEmpty() == true,
+                                tileIcon = walletController.walletClient.tileIcon,
+                            ),
+                            TAG,
+                        )
+                    }
+
+                    override fun onWalletCardRetrievalError(error: GetWalletCardsError?) {
+                        Log.e(TAG, "Wallet card retrieval error, message: \"${error?.message}\"")
+                        trySendWithFailureLogging(
+                            KeyguardQuickAffordanceConfig.LockScreenState.Hidden,
+                            TAG,
+                        )
+                    }
+                }
+
+            walletController.setupWalletChangeObservers(
+                callback,
+                QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
+                QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
+            )
+            walletController.updateWalletPreference()
+            walletController.queryWalletCards(callback)
+
+            awaitClose {
+                walletController.unregisterWalletChangeObservers(
+                    QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
+                    QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
+                )
+            }
+        }
+
+    override fun onTriggered(
+        expandable: Expandable?,
+    ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+        walletController.startQuickAccessUiIntent(
+            activityStarter,
+            expandable?.activityLaunchController(),
+            /* hasCard= */ true,
+        )
+        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+    }
+
+    private fun state(
+        isFeatureEnabled: Boolean,
+        hasCard: Boolean,
+        tileIcon: Drawable?,
+    ): KeyguardQuickAffordanceConfig.LockScreenState {
+        return if (isFeatureEnabled && hasCard && tileIcon != null) {
+            KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                icon =
+                    Icon.Loaded(
+                        drawable = tileIcon,
+                        contentDescription =
+                            ContentDescription.Resource(
+                                res = R.string.accessibility_wallet_button,
+                            ),
+                    ),
+            )
+        } else {
+            KeyguardQuickAffordanceConfig.LockScreenState.Hidden
+        }
+    }
+
+    companion object {
+        private const val TAG = "QuickAccessWalletKeyguardQuickAffordanceConfig"
+    }
+}
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 b186ae0..6baaf5f 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
@@ -21,7 +21,10 @@
 import com.android.systemui.common.shared.model.Position
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.doze.DozeHost
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness
 import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import javax.inject.Inject
@@ -89,6 +92,9 @@
     /** Observable for the [StatusBarState] */
     val statusBarState: Flow<StatusBarState>
 
+    /** Observable for device wake/sleep state */
+    val wakefulnessState: Flow<WakefulnessModel>
+
     /**
      * Returns `true` if the keyguard is showing; `false` otherwise.
      *
@@ -118,6 +124,7 @@
     statusBarStateController: StatusBarStateController,
     private val keyguardStateController: KeyguardStateController,
     dozeHost: DozeHost,
+    wakefulnessLifecycle: WakefulnessLifecycle,
 ) : KeyguardRepository {
     private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
     override val animateBottomAreaDozingTransitions =
@@ -207,6 +214,40 @@
         awaitClose { statusBarStateController.removeCallback(callback) }
     }
 
+    override val wakefulnessState: Flow<WakefulnessModel> = conflatedCallbackFlow {
+        val callback =
+            object : WakefulnessLifecycle.Observer {
+                override fun onStartedWakingUp() {
+                    trySendWithFailureLogging(
+                        WakefulnessModel.STARTING_TO_WAKE,
+                        TAG,
+                        "Wakefulness: starting to wake"
+                    )
+                }
+                override fun onFinishedWakingUp() {
+                    trySendWithFailureLogging(WakefulnessModel.AWAKE, TAG, "Wakefulness: awake")
+                }
+                override fun onStartedGoingToSleep() {
+                    trySendWithFailureLogging(
+                        WakefulnessModel.STARTING_TO_SLEEP,
+                        TAG,
+                        "Wakefulness: starting to sleep"
+                    )
+                }
+                override fun onFinishedGoingToSleep() {
+                    trySendWithFailureLogging(WakefulnessModel.ASLEEP, TAG, "Wakefulness: asleep")
+                }
+            }
+        wakefulnessLifecycle.addObserver(callback)
+        trySendWithFailureLogging(
+            wakefulnessIntToObject(wakefulnessLifecycle.getWakefulness()),
+            TAG,
+            "initial wakefulness state"
+        )
+
+        awaitClose { wakefulnessLifecycle.removeObserver(callback) }
+    }
+
     override fun setAnimateDozingTransitions(animate: Boolean) {
         _animateBottomAreaDozingTransitions.value = animate
     }
@@ -228,6 +269,16 @@
         }
     }
 
+    private fun wakefulnessIntToObject(@Wakefulness value: Int): WakefulnessModel {
+        return when (value) {
+            0 -> WakefulnessModel.ASLEEP
+            1 -> WakefulnessModel.STARTING_TO_WAKE
+            2 -> WakefulnessModel.AWAKE
+            3 -> WakefulnessModel.STARTING_TO_SLEEP
+            else -> throw IllegalArgumentException("Invalid Wakefulness value: $value")
+        }
+    }
+
     companion object {
         private const val TAG = "KeyguardRepositoryImpl"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index d15d7f2..0c72520 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -22,4 +22,9 @@
 @Module
 interface KeyguardRepositoryModule {
     @Binds fun keyguardRepository(impl: KeyguardRepositoryImpl): KeyguardRepository
+
+    @Binds
+    fun keyguardTransitionRepository(
+        impl: KeyguardTransitionRepositoryImpl
+    ): KeyguardTransitionRepository
 }
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 ab25597..e3d1a27 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
@@ -29,27 +29,33 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import java.util.UUID
 import javax.inject.Inject
+import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 
-@SysUISingleton
-class KeyguardTransitionRepository @Inject constructor() {
-    /*
-     * Each transition between [KeyguardState]s will have an associated Flow.
-     * In order to collect these events, clients should call [transition].
+/**
+ * The source of truth for all keyguard transitions.
+ *
+ * While the keyguard component is visible, it can undergo a number of transitions between different
+ * UI screens, such as AOD (Always-on Display), Bouncer, and others mentioned in [KeyguardState].
+ * These UI elements should listen to events emitted by [transitions], to ensure a centrally
+ * coordinated experience.
+ *
+ * To create or modify logic that controls when and how transitions get created, look at
+ * [TransitionInteractor]. These interactors will call [startTransition] and [updateTransition] on
+ * this repository.
+ */
+interface KeyguardTransitionRepository {
+    /**
+     * All events regarding transitions, as they start, run, and complete. [TransitionStep#value] is
+     * a float between [0, 1] representing progress towards completion. If this is a user driven
+     * transition, that value may not be a monotonic progression, as the user may swipe in any
+     * direction.
      */
-    private val _transitions = MutableStateFlow(TransitionStep())
-    val transitions = _transitions.asStateFlow()
-
-    /* Information about the active transition. */
-    private var currentTransitionInfo: TransitionInfo? = null
-    /*
-     * When manual control of the transition is requested, a unique [UUID] is used as the handle
-     * to permit calls to [updateTransition]
-     */
-    private var updateTransitionId: UUID? = null
+    val transitions: Flow<TransitionStep>
 
     /**
      * Interactors that require information about changes between [KeyguardState]s will call this to
@@ -60,65 +66,10 @@
     }
 
     /**
-     * Begin a transition from one state to another. The [info.from] must match
-     * [currentTransitionInfo.to], or the request will be denied. This is enforced to avoid
-     * unplanned transitions.
+     * Begin a transition from one state to another. Will not start if another transition is in
+     * progress.
      */
-    fun startTransition(info: TransitionInfo): UUID? {
-        if (currentTransitionInfo != null) {
-            // Open questions:
-            // * Queue of transitions? buffer of 1?
-            // * Are transitions cancellable if a new one is triggered?
-            // * What validation does this need to do?
-            Log.wtf(TAG, "Transition still active: $currentTransitionInfo")
-            return null
-        }
-        currentTransitionInfo?.animator?.cancel()
-
-        currentTransitionInfo = info
-        info.animator?.let { animator ->
-            // An animator was provided, so use it to run the transition
-            animator.setFloatValues(0f, 1f)
-            val updateListener =
-                object : AnimatorUpdateListener {
-                    override fun onAnimationUpdate(animation: ValueAnimator) {
-                        emitTransition(
-                            info,
-                            (animation.getAnimatedValue() as Float),
-                            TransitionState.RUNNING
-                        )
-                    }
-                }
-            val adapter =
-                object : AnimatorListenerAdapter() {
-                    override fun onAnimationStart(animation: Animator) {
-                        Log.i(TAG, "Starting transition: $info")
-                        emitTransition(info, 0f, TransitionState.STARTED)
-                    }
-                    override fun onAnimationCancel(animation: Animator) {
-                        Log.i(TAG, "Cancelling transition: $info")
-                    }
-                    override fun onAnimationEnd(animation: Animator) {
-                        Log.i(TAG, "Ending transition: $info")
-                        emitTransition(info, 1f, TransitionState.FINISHED)
-                        animator.removeListener(this)
-                        animator.removeUpdateListener(updateListener)
-                    }
-                }
-            animator.addListener(adapter)
-            animator.addUpdateListener(updateListener)
-            animator.start()
-            return@startTransition null
-        }
-            ?: run {
-                Log.i(TAG, "Starting transition (manual): $info")
-                emitTransition(info, 0f, TransitionState.STARTED)
-
-                // No animator, so it's manual. Provide a mechanism to callback
-                updateTransitionId = UUID.randomUUID()
-                return@startTransition updateTransitionId
-            }
-    }
+    fun startTransition(info: TransitionInfo): UUID?
 
     /**
      * Allows manual control of a transition. When calling [startTransition], the consumer must pass
@@ -132,58 +83,127 @@
         transitionId: UUID,
         @FloatRange(from = 0.0, to = 1.0) value: Float,
         state: TransitionState
+    )
+}
+
+@SysUISingleton
+class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitionRepository {
+    /*
+     * Each transition between [KeyguardState]s will have an associated Flow.
+     * In order to collect these events, clients should call [transition].
+     */
+    private val _transitions =
+        MutableSharedFlow<TransitionStep>(
+            extraBufferCapacity = 10,
+            onBufferOverflow = BufferOverflow.DROP_OLDEST
+        )
+    override val transitions = _transitions.asSharedFlow().distinctUntilChanged()
+    private var lastStep: TransitionStep = TransitionStep()
+
+    /*
+     * When manual control of the transition is requested, a unique [UUID] is used as the handle
+     * to permit calls to [updateTransition]
+     */
+    private var updateTransitionId: UUID? = null
+
+    override fun startTransition(info: TransitionInfo): UUID? {
+        if (lastStep.transitionState != TransitionState.FINISHED) {
+            // Open questions:
+            // * Queue of transitions? buffer of 1?
+            // * Are transitions cancellable if a new one is triggered?
+            // * What validation does this need to do?
+            Log.wtf(TAG, "Transition still active: $lastStep")
+            return null
+        }
+
+        info.animator?.let { animator ->
+            // An animator was provided, so use it to run the transition
+            animator.setFloatValues(0f, 1f)
+            val updateListener =
+                object : AnimatorUpdateListener {
+                    override fun onAnimationUpdate(animation: ValueAnimator) {
+                        emitTransition(
+                            TransitionStep(
+                                info,
+                                (animation.getAnimatedValue() as Float),
+                                TransitionState.RUNNING
+                            )
+                        )
+                    }
+                }
+            val adapter =
+                object : AnimatorListenerAdapter() {
+                    override fun onAnimationStart(animation: Animator) {
+                        emitTransition(TransitionStep(info, 0f, TransitionState.STARTED))
+                    }
+                    override fun onAnimationCancel(animation: Animator) {
+                        Log.i(TAG, "Cancelling transition: $info")
+                    }
+                    override fun onAnimationEnd(animation: Animator) {
+                        emitTransition(TransitionStep(info, 1f, TransitionState.FINISHED))
+                        animator.removeListener(this)
+                        animator.removeUpdateListener(updateListener)
+                    }
+                }
+            animator.addListener(adapter)
+            animator.addUpdateListener(updateListener)
+            animator.start()
+            return@startTransition null
+        }
+            ?: run {
+                emitTransition(TransitionStep(info, 0f, TransitionState.STARTED))
+
+                // No animator, so it's manual. Provide a mechanism to callback
+                updateTransitionId = UUID.randomUUID()
+                return@startTransition updateTransitionId
+            }
+    }
+
+    override fun updateTransition(
+        transitionId: UUID,
+        @FloatRange(from = 0.0, to = 1.0) value: Float,
+        state: TransitionState
     ) {
         if (updateTransitionId != transitionId) {
             Log.wtf(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
             return
         }
 
-        if (currentTransitionInfo == null) {
-            Log.wtf(TAG, "Attempting to update with null 'currentTransitionInfo'")
-            return
+        if (state == TransitionState.FINISHED) {
+            updateTransitionId = null
         }
 
-        currentTransitionInfo?.let { info ->
-            if (state == TransitionState.FINISHED) {
-                updateTransitionId = null
-                Log.i(TAG, "Ending transition: $info")
-            }
-
-            emitTransition(info, value, state)
-        }
+        val nextStep = lastStep.copy(value = value, transitionState = state)
+        emitTransition(nextStep, isManual = true)
     }
 
-    private fun emitTransition(
-        info: TransitionInfo,
-        value: Float,
-        transitionState: TransitionState
-    ) {
-        trace(info, transitionState)
-
-        if (transitionState == TransitionState.FINISHED) {
-            currentTransitionInfo = null
+    private fun emitTransition(nextStep: TransitionStep, isManual: Boolean = false) {
+        trace(nextStep, isManual)
+        val emitted = _transitions.tryEmit(nextStep)
+        if (!emitted) {
+            Log.w(TAG, "Failed to emit next value without suspending")
         }
-        _transitions.value = TransitionStep(info.from, info.to, value, transitionState)
+        lastStep = nextStep
     }
 
-    private fun trace(info: TransitionInfo, transitionState: TransitionState) {
+    private fun trace(step: TransitionStep, isManual: Boolean) {
         if (
-            transitionState != TransitionState.STARTED &&
-                transitionState != TransitionState.FINISHED
+            step.transitionState != TransitionState.STARTED &&
+                step.transitionState != TransitionState.FINISHED
         ) {
             return
         }
         val traceName =
-            "Transition: ${info.from} -> ${info.to} " +
-                if (info.animator == null) {
+            "Transition: ${step.from} -> ${step.to} " +
+                if (isManual) {
                     "(manual)"
                 } else {
                     ""
                 }
         val traceCookie = traceName.hashCode()
-        if (transitionState == TransitionState.STARTED) {
+        if (step.transitionState == TransitionState.STARTED) {
             Trace.beginAsyncSection(traceName, traceCookie)
-        } else if (transitionState == TransitionState.FINISHED) {
+        } else if (step.transitionState == TransitionState.FINISHED) {
             Trace.endAsyncSection(traceName, traceCookie)
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
index 4003766..0aeff7f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.collect
@@ -36,31 +37,35 @@
     @Application private val scope: CoroutineScope,
     private val keyguardRepository: KeyguardRepository,
     private val keyguardTransitionRepository: KeyguardTransitionRepository,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
 ) : TransitionInteractor("AOD<->LOCKSCREEN") {
 
     override fun start() {
         scope.launch {
-            keyguardRepository.isDozing.collect { isDozing ->
-                if (isDozing) {
-                    keyguardTransitionRepository.startTransition(
-                        TransitionInfo(
-                            name,
-                            KeyguardState.LOCKSCREEN,
-                            KeyguardState.AOD,
-                            getAnimator(),
+            keyguardRepository.isDozing
+                .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+                .collect { pair ->
+                    val (isDozing, keyguardState) = pair
+                    if (isDozing && keyguardState == KeyguardState.LOCKSCREEN) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                name,
+                                KeyguardState.LOCKSCREEN,
+                                KeyguardState.AOD,
+                                getAnimator(),
+                            )
                         )
-                    )
-                } else {
-                    keyguardTransitionRepository.startTransition(
-                        TransitionInfo(
-                            name,
-                            KeyguardState.AOD,
-                            KeyguardState.LOCKSCREEN,
-                            getAnimator(),
+                    } else if (!isDozing && keyguardState == KeyguardState.AOD) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                name,
+                                KeyguardState.AOD,
+                                KeyguardState.LOCKSCREEN,
+                                getAnimator(),
+                            )
                         )
-                    )
+                    }
                 }
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
new file mode 100644
index 0000000..0e2a54c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class GoneAodTransitionInteractor
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    private val keyguardInteractor: KeyguardInteractor,
+    private val keyguardTransitionRepository: KeyguardTransitionRepository,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) : TransitionInteractor("GONE->AOD") {
+
+    override fun start() {
+        scope.launch {
+            keyguardInteractor.wakefulnessState
+                .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+                .collect { pair ->
+                    val (wakefulnessState, keyguardState) = pair
+                    if (
+                        keyguardState == KeyguardState.GONE &&
+                            wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP
+                    ) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                name,
+                                KeyguardState.GONE,
+                                KeyguardState.AOD,
+                                getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
+    private fun getAnimator(): ValueAnimator {
+        return ValueAnimator().apply {
+            setInterpolator(Interpolators.LINEAR)
+            setDuration(TRANSITION_DURATION_MS)
+        }
+    }
+
+    companion object {
+        private const val TRANSITION_DURATION_MS = 500L
+    }
+}
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 fc2269c..03c6a78 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -19,6 +19,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 
@@ -40,6 +41,8 @@
     val isDozing: Flow<Boolean> = repository.isDozing
     /** Whether the keyguard is showing to not. */
     val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+    /** The device wake/sleep state */
+    val wakefulnessState: Flow<WakefulnessModel> = repository.wakefulnessState
 
     fun isKeyguardShowing(): Boolean {
         return repository.isKeyguardShowing()
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 f663b0d..13d97aa 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
@@ -21,15 +21,14 @@
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
-import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import javax.inject.Inject
-import kotlin.reflect.KClass
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.onStart
@@ -63,25 +62,25 @@
     }
 
     /**
-     * Notifies that a quick affordance has been clicked by the user.
+     * Notifies that a quick affordance has been "triggered" (clicked) by the user.
      *
      * @param configKey The configuration key corresponding to the [KeyguardQuickAffordanceModel] of
      * the affordance that was clicked
      * @param expandable An optional [Expandable] for the activity- or dialog-launch animation
      */
-    fun onQuickAffordanceClicked(
-        configKey: KClass<out KeyguardQuickAffordanceConfig>,
+    fun onQuickAffordanceTriggered(
+        configKey: String,
         expandable: Expandable?,
     ) {
-        @Suppress("UNCHECKED_CAST") val config = registry.get(configKey as KClass<Nothing>)
-        when (val result = config.onQuickAffordanceClicked(expandable)) {
-            is KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity ->
+        @Suppress("UNCHECKED_CAST") val config = registry.get(configKey)
+        when (val result = config.onTriggered(expandable)) {
+            is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity ->
                 launchQuickAffordance(
                     intent = result.intent,
                     canShowWhileLocked = result.canShowWhileLocked,
                     expandable = expandable,
                 )
-            is KeyguardQuickAffordanceConfig.OnClickedResult.Handled -> Unit
+            is KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled -> Unit
         }
     }
 
@@ -95,16 +94,20 @@
                 // value and avoid subtle bugs where the downstream isn't receiving any values
                 // because one config implementation is not emitting an initial value. For example,
                 // see b/244296596.
-                config.state.onStart { emit(KeyguardQuickAffordanceConfig.State.Hidden) }
+                config.lockScreenState.onStart {
+                    emit(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+                }
             }
         ) { states ->
-            val index = states.indexOfFirst { it is KeyguardQuickAffordanceConfig.State.Visible }
+            val index =
+                states.indexOfFirst { it is KeyguardQuickAffordanceConfig.LockScreenState.Visible }
             if (index != -1) {
-                val visibleState = states[index] as KeyguardQuickAffordanceConfig.State.Visible
+                val visibleState =
+                    states[index] as KeyguardQuickAffordanceConfig.LockScreenState.Visible
                 KeyguardQuickAffordanceModel.Visible(
-                    configKey = configs[index]::class,
+                    configKey = configs[index].key,
                     icon = visibleState.icon,
-                    toggle = visibleState.toggle,
+                    activationState = visibleState.activationState,
                 )
             } else {
                 KeyguardQuickAffordanceModel.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
new file mode 100644
index 0000000..83d9432
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import com.android.keyguard.logging.KeyguardLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+/** Collect flows of interest for auditing keyguard transitions. */
+@SysUISingleton
+class KeyguardTransitionAuditLogger
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    private val interactor: KeyguardTransitionInteractor,
+    private val keyguardInteractor: KeyguardInteractor,
+    private val logger: KeyguardLogger,
+) {
+
+    fun start() {
+        scope.launch {
+            keyguardInteractor.wakefulnessState.collect { logger.v("WakefulnessState", it) }
+        }
+
+        scope.launch {
+            interactor.finishedKeyguardState.collect { logger.i("Finished transition to", it) }
+        }
+
+        scope.launch {
+            interactor.startedKeyguardState.collect { logger.i("Started transition to", it) }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index b166681..d5ea77b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -26,6 +26,7 @@
 @Inject
 constructor(
     private val interactors: Set<TransitionInteractor>,
+    private val auditLogger: KeyguardTransitionAuditLogger,
 ) : CoreStartable {
 
     override fun start() {
@@ -38,9 +39,12 @@
                 when (it) {
                     is LockscreenBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
                     is AodLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
+                    is GoneAodTransitionInteractor -> Log.d(TAG, "Started $it")
+                    is LockscreenGoneTransitionInteractor -> Log.d(TAG, "Started $it")
                 }
             it.start()
         }
+        auditLogger.start()
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 7409aec..dffd097 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -19,11 +19,15 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 
@@ -40,6 +44,9 @@
     /** LOCKSCREEN->AOD transition information. */
     val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
 
+    /** GONE->AOD information. */
+    val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
+
     /**
      * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
      * Lockscreen (0f).
@@ -49,4 +56,16 @@
             aodToLockscreenTransition.map { step -> step.copy(value = 1f - step.value) },
             lockscreenToAodTransition,
         )
+
+    /* The last completed [KeyguardState] transition */
+    val finishedKeyguardState: Flow<KeyguardState> =
+        repository.transitions
+            .filter { step -> step.transitionState == TransitionState.FINISHED }
+            .map { step -> step.to }
+
+    /* The last started [KeyguardState] transition */
+    val startedKeyguardState: Flow<KeyguardState> =
+        repository.transitions
+            .filter { step -> step.transitionState == TransitionState.STARTED }
+            .map { step -> step.to }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index 3c2a12e..761f3fd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -29,6 +29,7 @@
 import java.util.UUID
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
@@ -40,59 +41,63 @@
     private val keyguardRepository: KeyguardRepository,
     private val shadeRepository: ShadeRepository,
     private val keyguardTransitionRepository: KeyguardTransitionRepository,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor
 ) : TransitionInteractor("LOCKSCREEN<->BOUNCER") {
 
     private var transitionId: UUID? = null
 
     override fun start() {
         scope.launch {
-            shadeRepository.shadeModel.sample(
-                combine(
-                    keyguardTransitionRepository.transitions,
-                    keyguardRepository.statusBarState,
-                ) { transitions, statusBarState ->
-                    Pair(transitions, statusBarState)
-                }
-            ) { shadeModel, pair ->
-                val (transitions, statusBarState) = pair
+            shadeRepository.shadeModel
+                .sample(
+                    combine(
+                        keyguardTransitionInteractor.finishedKeyguardState,
+                        keyguardRepository.statusBarState,
+                    ) { keyguardState, statusBarState ->
+                        Pair(keyguardState, statusBarState)
+                    },
+                    { shadeModel, pair -> Triple(shadeModel, pair.first, pair.second) }
+                )
+                .collect { triple ->
+                    val (shadeModel, keyguardState, statusBarState) = triple
 
-                val id = transitionId
-                if (id != null) {
-                    // An existing `id` means a transition is started, and calls to
-                    // `updateTransition` will control it until FINISHED
-                    keyguardTransitionRepository.updateTransition(
-                        id,
-                        shadeModel.expansionAmount,
-                        if (shadeModel.expansionAmount == 0f || shadeModel.expansionAmount == 1f) {
-                            transitionId = null
-                            TransitionState.FINISHED
-                        } else {
-                            TransitionState.RUNNING
-                        }
-                    )
-                } else {
-                    // TODO (b/251849525): Remove statusbarstate check when that state is integrated
-                    // into KeyguardTransitionRepository
-                    val isOnLockscreen =
-                        transitions.transitionState == TransitionState.FINISHED &&
-                            transitions.to == KeyguardState.LOCKSCREEN
-                    if (
-                        isOnLockscreen &&
-                            shadeModel.isUserDragging &&
-                            statusBarState != SHADE_LOCKED
-                    ) {
-                        transitionId =
-                            keyguardTransitionRepository.startTransition(
-                                TransitionInfo(
-                                    ownerName = name,
-                                    from = KeyguardState.LOCKSCREEN,
-                                    to = KeyguardState.BOUNCER,
-                                    animator = null,
+                    val id = transitionId
+                    if (id != null) {
+                        // An existing `id` means a transition is started, and calls to
+                        // `updateTransition` will control it until FINISHED
+                        keyguardTransitionRepository.updateTransition(
+                            id,
+                            shadeModel.expansionAmount,
+                            if (
+                                shadeModel.expansionAmount == 0f || shadeModel.expansionAmount == 1f
+                            ) {
+                                transitionId = null
+                                TransitionState.FINISHED
+                            } else {
+                                TransitionState.RUNNING
+                            }
+                        )
+                    } else {
+                        // TODO (b/251849525): Remove statusbarstate check when that state is
+                        // integrated
+                        // into KeyguardTransitionRepository
+                        if (
+                            keyguardState == KeyguardState.LOCKSCREEN &&
+                                shadeModel.isUserDragging &&
+                                statusBarState != SHADE_LOCKED
+                        ) {
+                            transitionId =
+                                keyguardTransitionRepository.startTransition(
+                                    TransitionInfo(
+                                        ownerName = name,
+                                        from = KeyguardState.LOCKSCREEN,
+                                        to = KeyguardState.BOUNCER,
+                                        animator = null,
+                                    )
                                 )
-                            )
+                        }
                     }
                 }
-            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt
new file mode 100644
index 0000000..6c1adbd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class LockscreenGoneTransitionInteractor
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    private val keyguardInteractor: KeyguardInteractor,
+    private val keyguardTransitionRepository: KeyguardTransitionRepository,
+) : TransitionInteractor("LOCKSCREEN->GONE") {
+
+    override fun start() {
+        scope.launch {
+            keyguardInteractor.isKeyguardShowing.collect { isShowing ->
+                if (!isShowing) {
+                    keyguardTransitionRepository.startTransition(
+                        TransitionInfo(
+                            name,
+                            KeyguardState.LOCKSCREEN,
+                            KeyguardState.GONE,
+                            getAnimator(),
+                        )
+                    )
+                }
+            }
+        }
+    }
+
+    private fun getAnimator(): ValueAnimator {
+        return ValueAnimator().apply {
+            setInterpolator(Interpolators.LINEAR)
+            setDuration(TRANSITION_DURATION_MS)
+        }
+    }
+
+    companion object {
+        private const val TRANSITION_DURATION_MS = 10L
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index 74c542c..728bafa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -39,4 +39,10 @@
     @Binds
     @IntoSet
     abstract fun aodLockscreen(impl: AodLockscreenTransitionInteractor): TransitionInteractor
+
+    @Binds @IntoSet abstract fun goneAod(impl: GoneAodTransitionInteractor): TransitionInteractor
+
+    @Binds
+    @IntoSet
+    abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
index e56b259..32560af 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
@@ -18,9 +18,7 @@
 package com.android.systemui.keyguard.domain.model
 
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordanceToggleState
-import kotlin.reflect.KClass
+import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 
 /**
  * Models a "quick affordance" in the keyguard bottom area (for example, a button on the
@@ -33,10 +31,10 @@
     /** A affordance is visible. */
     data class Visible(
         /** Identifier for the affordance this is modeling. */
-        val configKey: KClass<out KeyguardQuickAffordanceConfig>,
+        val configKey: String,
         /** An icon for the affordance. */
         val icon: Icon,
-        /** The toggle state for the affordance. */
-        val toggle: KeyguardQuickAffordanceToggleState,
+        /** The activation state of the affordance. */
+        val activationState: ActivationState,
     ) : KeyguardQuickAffordanceModel()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt
deleted file mode 100644
index 95027d0..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ /dev/null
@@ -1,69 +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.keyguard.domain.quickaffordance
-
-import android.content.Intent
-import com.android.systemui.animation.Expandable
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordanceToggleState
-import kotlinx.coroutines.flow.Flow
-
-/** Defines interface that can act as data source for a single quick affordance model. */
-interface KeyguardQuickAffordanceConfig {
-
-    val state: Flow<State>
-
-    fun onQuickAffordanceClicked(expandable: Expandable?): OnClickedResult
-
-    /**
-     * Encapsulates the state of a "quick affordance" in the keyguard bottom area (for example, a
-     * button on the lock-screen).
-     */
-    sealed class State {
-
-        /** No affordance should show up. */
-        object Hidden : State()
-
-        /** An affordance is visible. */
-        data class Visible(
-            /** An icon for the affordance. */
-            val icon: Icon,
-            /** The toggle state for the affordance. */
-            val toggle: KeyguardQuickAffordanceToggleState =
-                KeyguardQuickAffordanceToggleState.NotSupported,
-        ) : State()
-    }
-
-    sealed class OnClickedResult {
-        /**
-         * Returning this as a result from the [onQuickAffordanceClicked] method means that the
-         * implementation has taken care of the click, the system will do nothing.
-         */
-        object Handled : OnClickedResult()
-
-        /**
-         * Returning this as a result from the [onQuickAffordanceClicked] method means that the
-         * implementation has _not_ taken care of the click and the system should start an activity
-         * using the given [Intent].
-         */
-        data class StartActivity(
-            val intent: Intent,
-            val canShowWhileLocked: Boolean,
-        ) : OnClickedResult()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt
index 94024d4..b48acb6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.domain.quickaffordance
 
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import dagger.Binds
 import dagger.Module
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt
index ad40ee7..8526ada 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt
@@ -17,14 +17,17 @@
 
 package com.android.systemui.keyguard.domain.quickaffordance
 
-import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.data.quickaffordance.HomeControlsKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.QrCodeScannerKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.QuickAccessWalletKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import javax.inject.Inject
-import kotlin.reflect.KClass
 
 /** Central registry of all known quick affordance configs. */
 interface KeyguardQuickAffordanceRegistry<T : KeyguardQuickAffordanceConfig> {
     fun getAll(position: KeyguardQuickAffordancePosition): List<T>
-    fun get(configClass: KClass<out T>): T
+    fun get(key: String): T
 }
 
 class KeyguardQuickAffordanceRegistryImpl
@@ -46,8 +49,8 @@
                     qrCodeScanner,
                 ),
         )
-    private val configByClass =
-        configsByPosition.values.flatten().associateBy { config -> config::class }
+    private val configByKey =
+        configsByPosition.values.flatten().associateBy { config -> config.key }
 
     override fun getAll(
         position: KeyguardQuickAffordancePosition,
@@ -56,8 +59,8 @@
     }
 
     override fun get(
-        configClass: KClass<out KeyguardQuickAffordanceConfig>
+        key: String,
     ): KeyguardQuickAffordanceConfig {
-        return configByClass.getValue(configClass)
+        return configByKey.getValue(key)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
deleted file mode 100644
index 502a607..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ /dev/null
@@ -1,97 +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.keyguard.domain.quickaffordance
-
-import com.android.systemui.R
-import com.android.systemui.animation.Expandable
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
-import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-
-/** QR code scanner quick affordance data source. */
-@SysUISingleton
-class QrCodeScannerKeyguardQuickAffordanceConfig
-@Inject
-constructor(
-    private val controller: QRCodeScannerController,
-) : KeyguardQuickAffordanceConfig {
-
-    override val state: Flow<KeyguardQuickAffordanceConfig.State> = conflatedCallbackFlow {
-        val callback =
-            object : QRCodeScannerController.Callback {
-                override fun onQRCodeScannerActivityChanged() {
-                    trySendWithFailureLogging(state(), TAG)
-                }
-                override fun onQRCodeScannerPreferenceChanged() {
-                    trySendWithFailureLogging(state(), TAG)
-                }
-            }
-
-        controller.addCallback(callback)
-        controller.registerQRCodeScannerChangeObservers(
-            QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
-            QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE
-        )
-        // Registering does not push an initial update.
-        trySendWithFailureLogging(state(), "initial state", TAG)
-
-        awaitClose {
-            controller.unregisterQRCodeScannerChangeObservers(
-                QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
-                QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE
-            )
-            controller.removeCallback(callback)
-        }
-    }
-
-    override fun onQuickAffordanceClicked(
-        expandable: Expandable?,
-    ): KeyguardQuickAffordanceConfig.OnClickedResult {
-        return KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity(
-            intent = controller.intent,
-            canShowWhileLocked = true,
-        )
-    }
-
-    private fun state(): KeyguardQuickAffordanceConfig.State {
-        return if (controller.isEnabledForLockScreenButton) {
-            KeyguardQuickAffordanceConfig.State.Visible(
-                icon =
-                    Icon.Resource(
-                        res = R.drawable.ic_qr_code_scanner,
-                        contentDescription =
-                            ContentDescription.Resource(
-                                res = R.string.accessibility_qr_code_scanner_button,
-                            ),
-                    ),
-            )
-        } else {
-            KeyguardQuickAffordanceConfig.State.Hidden
-        }
-    }
-
-    companion object {
-        private const val TAG = "QrCodeScannerKeyguardQuickAffordanceConfig"
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
deleted file mode 100644
index a24a0d6..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ /dev/null
@@ -1,121 +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.keyguard.domain.quickaffordance
-
-import android.graphics.drawable.Drawable
-import android.service.quickaccesswallet.GetWalletCardsError
-import android.service.quickaccesswallet.GetWalletCardsResponse
-import android.service.quickaccesswallet.QuickAccessWalletClient
-import android.util.Log
-import com.android.systemui.R
-import com.android.systemui.animation.Expandable
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.wallet.controller.QuickAccessWalletController
-import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-
-/** Quick access wallet quick affordance data source. */
-@SysUISingleton
-class QuickAccessWalletKeyguardQuickAffordanceConfig
-@Inject
-constructor(
-    private val walletController: QuickAccessWalletController,
-    private val activityStarter: ActivityStarter,
-) : KeyguardQuickAffordanceConfig {
-
-    override val state: Flow<KeyguardQuickAffordanceConfig.State> = conflatedCallbackFlow {
-        val callback =
-            object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
-                override fun onWalletCardsRetrieved(response: GetWalletCardsResponse?) {
-                    trySendWithFailureLogging(
-                        state(
-                            isFeatureEnabled = walletController.isWalletEnabled,
-                            hasCard = response?.walletCards?.isNotEmpty() == true,
-                            tileIcon = walletController.walletClient.tileIcon,
-                        ),
-                        TAG,
-                    )
-                }
-
-                override fun onWalletCardRetrievalError(error: GetWalletCardsError?) {
-                    Log.e(TAG, "Wallet card retrieval error, message: \"${error?.message}\"")
-                    trySendWithFailureLogging(
-                        KeyguardQuickAffordanceConfig.State.Hidden,
-                        TAG,
-                    )
-                }
-            }
-
-        walletController.setupWalletChangeObservers(
-            callback,
-            QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
-            QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
-        )
-        walletController.updateWalletPreference()
-        walletController.queryWalletCards(callback)
-
-        awaitClose {
-            walletController.unregisterWalletChangeObservers(
-                QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
-                QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
-            )
-        }
-    }
-
-    override fun onQuickAffordanceClicked(
-        expandable: Expandable?,
-    ): KeyguardQuickAffordanceConfig.OnClickedResult {
-        walletController.startQuickAccessUiIntent(
-            activityStarter,
-            expandable?.activityLaunchController(),
-            /* hasCard= */ true,
-        )
-        return KeyguardQuickAffordanceConfig.OnClickedResult.Handled
-    }
-
-    private fun state(
-        isFeatureEnabled: Boolean,
-        hasCard: Boolean,
-        tileIcon: Drawable?,
-    ): KeyguardQuickAffordanceConfig.State {
-        return if (isFeatureEnabled && hasCard && tileIcon != null) {
-            KeyguardQuickAffordanceConfig.State.Visible(
-                icon =
-                    Icon.Loaded(
-                        drawable = tileIcon,
-                        contentDescription =
-                            ContentDescription.Resource(
-                                res = R.string.accessibility_wallet_button,
-                            ),
-                    ),
-            )
-        } else {
-            KeyguardQuickAffordanceConfig.State.Hidden
-        }
-    }
-
-    companion object {
-        private const val TAG = "QuickAccessWalletKeyguardQuickAffordanceConfig"
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index f66d5d3..7958033 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -17,7 +17,10 @@
 
 /** List of all possible states to transition to/from */
 enum class KeyguardState {
-    /** For initialization only */
+    /**
+     * For initialization as well as when the security method is set to NONE, indicating that
+     * the keyguard should never be shown.
+     */
     NONE,
     /* Always-on Display. The device is in a low-power mode with a minimal UI visible */
     AOD,
@@ -31,4 +34,11 @@
      * unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
      */
     LOCKSCREEN,
+
+    /*
+     * Keyguard is no longer visible. In most cases the user has just authenticated and keyguard
+     * is being removed, but there are other cases where the user is swiping away keyguard, such as
+     * with SWIPE security method or face unlock without bypass.
+     */
+    GONE,
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
index d8691c1..0e0465b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
@@ -17,7 +17,6 @@
 
 /** Possible states for a running transition between [State] */
 enum class TransitionState {
-    NONE,
     STARTED,
     RUNNING,
     FINISHED
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
index 688ec91..0ca3582 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
@@ -20,5 +20,11 @@
     val from: KeyguardState = KeyguardState.NONE,
     val to: KeyguardState = KeyguardState.NONE,
     val value: Float = 0f, // constrained [0.0, 1.0]
-    val transitionState: TransitionState = TransitionState.NONE,
-)
+    val transitionState: TransitionState = TransitionState.FINISHED,
+) {
+    constructor(
+        info: TransitionInfo,
+        value: Float,
+        transitionState: TransitionState,
+    ) : this(info.from, info.to, value, transitionState)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
new file mode 100644
index 0000000..64f834d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.keyguard.shared.model
+
+/** Model device wakefulness states. */
+enum class WakefulnessModel {
+    /** The device is asleep and not interactive. */
+    ASLEEP,
+    /** Received a signal that the device is beginning to wake up. */
+    STARTING_TO_WAKE,
+    /** Device is now fully awake and interactive. */
+    AWAKE,
+    /** Signal that the device is now going to sleep. */
+    STARTING_TO_SLEEP,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordanceToggleState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/ActivationState.kt
similarity index 68%
rename from packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordanceToggleState.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/ActivationState.kt
index 55d38a4..a68d190 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordanceToggleState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/ActivationState.kt
@@ -17,12 +17,12 @@
 
 package com.android.systemui.keyguard.shared.quickaffordance
 
-/** Enumerates all possible toggle states for a quick affordance on the lock-screen. */
-sealed class KeyguardQuickAffordanceToggleState {
-    /** Toggling is not supported. */
-    object NotSupported : KeyguardQuickAffordanceToggleState()
+/** Enumerates all possible activation states for a quick affordance on the lock-screen. */
+sealed class ActivationState {
+    /** Activation is not supported. */
+    object NotSupported : ActivationState()
     /** The quick affordance is on. */
-    object On : KeyguardQuickAffordanceToggleState()
+    object Active : ActivationState()
     /** The quick affordance is off. */
-    object Off : KeyguardQuickAffordanceToggleState()
+    object Inactive : ActivationState()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt
index 581dafa3..a18b036 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.model
+package com.android.systemui.keyguard.shared.quickaffordance
 
 /** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
 enum class KeyguardQuickAffordancePosition {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 535ca72..b6b2304 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -22,8 +22,8 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordanceToggleState
+import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -118,13 +118,13 @@
                     animateReveal = animateReveal,
                     icon = icon,
                     onClicked = { parameters ->
-                        quickAffordanceInteractor.onQuickAffordanceClicked(
+                        quickAffordanceInteractor.onQuickAffordanceTriggered(
                             configKey = parameters.configKey,
                             expandable = parameters.expandable,
                         )
                     },
                     isClickable = isClickable,
-                    isActivated = toggle is KeyguardQuickAffordanceToggleState.On,
+                    isActivated = activationState is ActivationState.Active,
                 )
             is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index bf598ba..44f48f9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -18,12 +18,10 @@
 
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
-import kotlin.reflect.KClass
 
 /** Models the UI state of a keyguard quick affordance button. */
 data class KeyguardQuickAffordanceViewModel(
-    val configKey: KClass<out KeyguardQuickAffordanceConfig>? = null,
+    val configKey: String? = null,
     val isVisible: Boolean = false,
     /** Whether to animate the transition of the quick affordance from invisible to visible. */
     val animateReveal: Boolean = false,
@@ -33,7 +31,7 @@
     val isActivated: Boolean = false,
 ) {
     data class OnClickedParameters(
-        val configKey: KClass<out KeyguardQuickAffordanceConfig>,
+        val configKey: String,
         val expandable: Expandable?,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
index ed649b1..f006442 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
@@ -181,6 +181,7 @@
         return enabled == other.enabled &&
             name == other.name &&
             intent == other.intent &&
-            id == other.id
+            id == other.id &&
+            showBroadcastButton == other.showBroadcastButton
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
index 2511324..a7f1b95 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
@@ -26,6 +26,7 @@
 import androidx.constraintlayout.widget.Barrier
 import com.android.systemui.R
 import com.android.systemui.media.controls.models.GutsViewHolder
+import com.android.systemui.ripple.MultiRippleView
 import com.android.systemui.util.animation.TransitionLayout
 
 private const val TAG = "MediaViewHolder"
@@ -36,6 +37,7 @@
 
     // Player information
     val albumView = itemView.requireViewById<ImageView>(R.id.album_art)
+    val multiRippleView = itemView.requireViewById<MultiRippleView>(R.id.touch_ripple_view)
     val appIcon = itemView.requireViewById<ImageView>(R.id.icon)
     val titleText = itemView.requireViewById<TextView>(R.id.header_title)
     val artistText = itemView.requireViewById<TextView>(R.id.header_artist)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
index 61ef2f1..918417f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
@@ -29,6 +29,7 @@
 import com.android.settingslib.Utils
 import com.android.systemui.media.controls.models.player.MediaViewHolder
 import com.android.systemui.monet.ColorScheme
+import com.android.systemui.ripple.MultiRippleController
 
 /**
  * A [ColorTransition] is an object that updates the colors of views each time [updateColorScheme]
@@ -100,12 +101,14 @@
 internal constructor(
     private val context: Context,
     private val mediaViewHolder: MediaViewHolder,
+    private val multiRippleController: MultiRippleController,
     animatingColorTransitionFactory: AnimatingColorTransitionFactory
 ) {
     constructor(
         context: Context,
-        mediaViewHolder: MediaViewHolder
-    ) : this(context, mediaViewHolder, ::AnimatingColorTransition)
+        mediaViewHolder: MediaViewHolder,
+        multiRippleController: MultiRippleController,
+    ) : this(context, mediaViewHolder, multiRippleController, ::AnimatingColorTransition)
 
     val bgColor = context.getColor(com.android.systemui.R.color.material_dynamic_secondary95)
     val surfaceColor =
@@ -125,6 +128,7 @@
             val accentColorList = ColorStateList.valueOf(accentPrimary)
             mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
             mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary)
+            multiRippleController.updateColor(accentPrimary)
         }
 
     val accentSecondary =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 18ecadb..5b14cf3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -76,6 +76,8 @@
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.media.controls.models.GutsViewHolder;
 import com.android.systemui.media.controls.models.player.MediaAction;
 import com.android.systemui.media.controls.models.player.MediaButton;
@@ -95,6 +97,10 @@
 import com.android.systemui.monet.Style;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.ripple.MultiRippleController;
+import com.android.systemui.ripple.RippleAnimation;
+import com.android.systemui.ripple.RippleAnimationConfig;
+import com.android.systemui.ripple.RippleShader;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -209,6 +215,8 @@
     private boolean mIsCurrentBroadcastedApp = false;
     private boolean mShowBroadcastDialogButton = false;
     private String mSwitchBroadcastApp;
+    private MultiRippleController mMultiRippleController;
+    private FeatureFlags mFeatureFlags;
 
     /**
      * Initialize a new control panel
@@ -236,7 +244,9 @@
             KeyguardStateController keyguardStateController,
             ActivityIntentHelper activityIntentHelper,
             NotificationLockscreenUserManager lockscreenUserManager,
-            BroadcastDialogController broadcastDialogController) {
+            BroadcastDialogController broadcastDialogController,
+            FeatureFlags featureFlags
+    ) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
         mMainExecutor = mainExecutor;
@@ -262,6 +272,8 @@
             logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT);
             return Unit.INSTANCE;
         });
+
+        mFeatureFlags = featureFlags;
     }
 
     /**
@@ -381,7 +393,9 @@
         AnimatorSet exit = loadAnimator(R.anim.media_metadata_exit,
                 Interpolators.EMPHASIZED_ACCELERATE, titleText, artistText);
 
-        mColorSchemeTransition = new ColorSchemeTransition(mContext, mMediaViewHolder);
+        mMultiRippleController = new MultiRippleController(vh.getMultiRippleView());
+        mColorSchemeTransition = new ColorSchemeTransition(
+                mContext, mMediaViewHolder, mMultiRippleController);
         mMetadataAnimationHandler = new MetadataAnimationHandler(exit, enter);
     }
 
@@ -982,6 +996,9 @@
                         mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId);
                         logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT);
                         action.run();
+                        if (mFeatureFlags.isEnabled(Flags.UMO_SURFACE_RIPPLE)) {
+                            mMultiRippleController.play(createTouchRippleAnimation(button));
+                        }
 
                         if (icon instanceof Animatable) {
                             ((Animatable) icon).start();
@@ -997,6 +1014,26 @@
         }
     }
 
+    private RippleAnimation createTouchRippleAnimation(ImageButton button) {
+        float maxSize = mMediaViewHolder.getMultiRippleView().getWidth() * 2;
+        return new RippleAnimation(
+                new RippleAnimationConfig(
+                        RippleShader.RippleShape.CIRCLE,
+                        /* duration= */ 1500L,
+                        /* centerX= */ button.getX() + button.getWidth() * 0.5f,
+                        /* centerY= */ button.getY() + button.getHeight() * 0.5f,
+                        /* maxWidth= */ maxSize,
+                        /* maxHeight= */ maxSize,
+                        /* pixelDensity= */ getContext().getResources().getDisplayMetrics().density,
+                        mColorSchemeTransition.getAccentPrimary().getTargetColor(),
+                        /* opacity= */ 100,
+                        /* shouldFillRipple= */ false,
+                        /* sparkleStrength= */ 0f,
+                        /* shouldDistort= */ false
+                )
+        );
+    }
+
     private void clearButton(final ImageButton button) {
         button.setImageDrawable(null);
         button.setContentDescription(null);
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index 6b46d8f..cbb670e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -43,7 +43,8 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.media.dream.MediaDreamComplication
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.NotifPanelEvents
+import com.android.systemui.shade.ShadeStateEvents
+import com.android.systemui.shade.ShadeStateEvents.ShadeStateEventsListener
 import com.android.systemui.statusbar.CrossFadeHelper
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -96,7 +97,7 @@
     private val dreamOverlayStateController: DreamOverlayStateController,
     configurationController: ConfigurationController,
     wakefulnessLifecycle: WakefulnessLifecycle,
-    panelEventsEvents: NotifPanelEvents,
+    panelEventsEvents: ShadeStateEvents,
     private val secureSettings: SecureSettings,
     @Main private val handler: Handler,
 ) {
@@ -534,8 +535,8 @@
             mediaHosts.forEach { it?.updateViewVisibility() }
         }
 
-        panelEventsEvents.registerListener(
-            object : NotifPanelEvents.Listener {
+        panelEventsEvents.addShadeStateEventsListener(
+            object : ShadeStateEventsListener {
                 override fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {
                     skipQqsOnExpansion = isExpandImmediateEnabled
                     updateDesiredLocation()
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
index 0a60437..769494a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -27,10 +27,11 @@
 /** Utility methods for media tap-to-transfer. */
 class MediaTttUtils {
     companion object {
-        // Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
-        // UpdateMediaTapToTransferReceiverDisplayTest
-        const val WINDOW_TITLE = "Media Transfer Chip View"
-        const val WAKE_REASON = "MEDIA_TRANSFER_ACTIVATED"
+        const val WINDOW_TITLE_SENDER = "Media Transfer Chip View (Sender)"
+        const val WINDOW_TITLE_RECEIVER = "Media Transfer Chip View (Receiver)"
+
+        const val WAKE_REASON_SENDER = "MEDIA_TRANSFER_ACTIVATED_SENDER"
+        const val WAKE_REASON_RECEIVER = "MEDIA_TRANSFER_ACTIVATED_RECEIVER"
 
         /**
          * Returns the information needed to display the icon in [Icon] form.
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index dc794e6..662d059 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -40,7 +40,6 @@
 import com.android.systemui.media.taptotransfer.common.MediaTttUtils
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
 import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
 import com.android.systemui.temporarydisplay.TemporaryViewInfo
 import com.android.systemui.util.animation.AnimationUtil.Companion.frames
@@ -78,8 +77,6 @@
         configurationController,
         powerManager,
         R.layout.media_ttt_chip_receiver,
-        MediaTttUtils.WINDOW_TITLE,
-        MediaTttUtils.WAKE_REASON,
 ) {
     @SuppressLint("WrongConstant") // We're allowed to use LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
     override val windowLayoutParams = commonWindowLayoutParams.apply {
@@ -192,7 +189,7 @@
     }
 
     private fun startRipple(rippleView: ReceiverChipRippleView) {
-        if (rippleView.rippleInProgress) {
+        if (rippleView.rippleInProgress()) {
             // Skip if ripple is still playing
             return
         }
@@ -231,7 +228,7 @@
 data class ChipReceiverInfo(
     val routeInfo: MediaRoute2Info,
     val appIconDrawableOverride: Drawable?,
-    val appNameOverride: CharSequence?
-) : TemporaryViewInfo {
-    override fun getTimeoutMs() = DEFAULT_TIMEOUT_MILLIS
-}
+    val appNameOverride: CharSequence?,
+    override val windowTitle: String = MediaTttUtils.WINDOW_TITLE_RECEIVER,
+    override val wakeReason: String = MediaTttUtils.WAKE_REASON_RECEIVER,
+) : TemporaryViewInfo()
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index 6e596ee..af7317c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -43,7 +43,7 @@
     @StringRes val stringResId: Int?,
     val transferStatus: TransferStatus,
     val endItem: SenderEndItem?,
-    val timeout: Long = DEFAULT_TIMEOUT_MILLIS
+    val timeout: Int = DEFAULT_TIMEOUT_MILLIS,
 ) {
     /**
      * A state representing that the two devices are close but not close enough to *start* a cast to
@@ -223,6 +223,6 @@
 // Give the Transfer*Triggered states a longer timeout since those states represent an active
 // process and we should keep the user informed about it as long as possible (but don't allow it to
 // continue indefinitely).
-private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 30000L
+private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 30000
 
 private const val TAG = "ChipStateSender"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 1fa8fae..d1ea2d0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -159,6 +159,9 @@
                     }
                 },
             vibrationEffect = chipStateSender.transferStatus.vibrationEffect,
+            windowTitle = MediaTttUtils.WINDOW_TITLE_SENDER,
+            wakeReason = MediaTttUtils.WAKE_REASON_SENDER,
+            timeoutMs = chipStateSender.timeout,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleController.kt b/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleController.kt
new file mode 100644
index 0000000..48df15c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleController.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.ripple
+
+import androidx.annotation.VisibleForTesting
+
+/** Controller that handles playing [RippleAnimation]. */
+class MultiRippleController(private val multipleRippleView: MultiRippleView) {
+
+    companion object {
+        /** Max number of ripple animations at a time. */
+        @VisibleForTesting const val MAX_RIPPLE_NUMBER = 10
+    }
+
+    /** Updates all the ripple colors during the animation. */
+    fun updateColor(color: Int) {
+        multipleRippleView.ripples.forEach { anim -> anim.updateColor(color) }
+    }
+
+    fun play(rippleAnimation: RippleAnimation) {
+        if (multipleRippleView.ripples.size >= MAX_RIPPLE_NUMBER) {
+            return
+        }
+
+        multipleRippleView.ripples.add(rippleAnimation)
+
+        // Remove ripple once the animation is done
+        rippleAnimation.play { multipleRippleView.ripples.remove(rippleAnimation) }
+
+        // Trigger drawing
+        multipleRippleView.invalidate()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleView.kt b/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleView.kt
new file mode 100644
index 0000000..c7f0b7e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleView.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.ripple
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.util.Log
+import android.view.View
+
+/**
+ * A view that allows multiple ripples to play.
+ *
+ * Use [MultiRippleController] to play ripple animations.
+ */
+class MultiRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
+
+    internal val ripples = ArrayList<RippleAnimation>()
+    private val ripplePaint = Paint()
+    private var isWarningLogged = false
+
+    companion object {
+        const val TAG = "MultiRippleView"
+    }
+
+    override fun onDraw(canvas: Canvas?) {
+        if (canvas == null || !canvas.isHardwareAccelerated) {
+            // Drawing with the ripple shader requires hardware acceleration, so skip
+            // if it's unsupported.
+            if (!isWarningLogged) {
+                // Only log once to not spam.
+                Log.w(
+                    TAG,
+                    "Can't draw ripple shader. $canvas does not support hardware acceleration."
+                )
+                isWarningLogged = true
+            }
+            return
+        }
+
+        var shouldInvalidate = false
+
+        ripples.forEach { anim ->
+            ripplePaint.shader = anim.rippleShader
+            canvas.drawPaint(ripplePaint)
+
+            shouldInvalidate = shouldInvalidate || anim.isPlaying()
+        }
+
+        if (shouldInvalidate) invalidate()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimation.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimation.kt
new file mode 100644
index 0000000..aca9e25
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimation.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.ripple
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import androidx.core.graphics.ColorUtils
+
+/** A single ripple animation. */
+class RippleAnimation(private val config: RippleAnimationConfig) {
+    internal val rippleShader: RippleShader = RippleShader(config.rippleShape)
+    private val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f)
+
+    init {
+        applyConfigToShader()
+    }
+
+    /** Updates the ripple color during the animation. */
+    fun updateColor(color: Int) {
+        config.apply { config.color = color }
+        applyConfigToShader()
+    }
+
+    @JvmOverloads
+    fun play(onAnimationEnd: Runnable? = null) {
+        if (isPlaying()) {
+            return // Ignore if ripple effect is already playing
+        }
+
+        animator.duration = config.duration
+        animator.addUpdateListener { updateListener ->
+            val now = updateListener.currentPlayTime
+            val progress = updateListener.animatedValue as Float
+            rippleShader.progress = progress
+            rippleShader.distortionStrength = if (config.shouldDistort) 1 - progress else 0f
+            rippleShader.time = now.toFloat()
+        }
+        animator.addListener(
+            object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator?) {
+                    onAnimationEnd?.run()
+                }
+            }
+        )
+        animator.start()
+    }
+
+    /** Indicates whether the animation is playing. */
+    fun isPlaying(): Boolean = animator.isRunning
+
+    private fun applyConfigToShader() {
+        rippleShader.setCenter(config.centerX, config.centerY)
+        rippleShader.setMaxSize(config.maxWidth, config.maxHeight)
+        rippleShader.rippleFill = config.shouldFillRipple
+        rippleShader.pixelDensity = config.pixelDensity
+        rippleShader.color = ColorUtils.setAlphaComponent(config.color, config.opacity)
+        rippleShader.sparkleStrength = config.sparkleStrength
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimationConfig.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimationConfig.kt
new file mode 100644
index 0000000..8812254
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimationConfig.kt
@@ -0,0 +1,32 @@
+package com.android.systemui.ripple
+
+import android.graphics.Color
+
+/**
+ * A struct that holds the ripple animation configurations.
+ *
+ * <p>This configuration is designed to play a SINGLE animation. Do not reuse or modify the
+ * configuration parameters to play different animations, unless the value has to change within the
+ * single animation (e.g. Change color or opacity during the animation). Note that this data class
+ * is pulled out to make the [RippleAnimation] constructor succinct.
+ */
+data class RippleAnimationConfig(
+    val rippleShape: RippleShader.RippleShape = RippleShader.RippleShape.CIRCLE,
+    val duration: Long = 0L,
+    val centerX: Float = 0f,
+    val centerY: Float = 0f,
+    val maxWidth: Float = 0f,
+    val maxHeight: Float = 0f,
+    val pixelDensity: Float = 1f,
+    var color: Int = Color.WHITE,
+    val opacity: Int = RIPPLE_DEFAULT_ALPHA,
+    val shouldFillRipple: Boolean = false,
+    val sparkleStrength: Float = RIPPLE_SPARKLE_STRENGTH,
+    val shouldDistort: Boolean = true
+) {
+    companion object {
+        const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
+        const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt()
+        const val RIPPLE_DEFAULT_ALPHA: Int = 45 // full opacity is 255.
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
index 1e51ffa..a6d7930 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
@@ -28,10 +28,6 @@
 import androidx.core.graphics.ColorUtils
 import com.android.systemui.ripple.RippleShader.RippleShape
 
-private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
-private const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt()
-const val RIPPLE_DEFAULT_ALPHA: Int = 45
-
 /**
  * A generic expanding ripple effect.
  *
@@ -45,8 +41,8 @@
         private set
 
     private val ripplePaint = Paint()
+    private val animator = ValueAnimator.ofFloat(0f, 1f)
 
-    var rippleInProgress: Boolean = false
     var duration: Long = 1750
 
     private var maxWidth: Float = 0.0f
@@ -80,9 +76,9 @@
         this.rippleShape = rippleShape
         rippleShader = RippleShader(rippleShape)
 
-        rippleShader.color = RIPPLE_DEFAULT_COLOR
+        rippleShader.color = RippleAnimationConfig.RIPPLE_DEFAULT_COLOR
         rippleShader.progress = 0f
-        rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
+        rippleShader.sparkleStrength = RippleAnimationConfig.RIPPLE_SPARKLE_STRENGTH
         rippleShader.pixelDensity = resources.displayMetrics.density
 
         ripplePaint.shader = rippleShader
@@ -90,10 +86,9 @@
 
     @JvmOverloads
     fun startRipple(onAnimationEnd: Runnable? = null) {
-        if (rippleInProgress) {
+        if (animator.isRunning) {
             return // Ignore if ripple effect is already playing
         }
-        val animator = ValueAnimator.ofFloat(0f, 1f)
         animator.duration = duration
         animator.addUpdateListener { updateListener ->
             val now = updateListener.currentPlayTime
@@ -105,19 +100,17 @@
         }
         animator.addListener(object : AnimatorListenerAdapter() {
             override fun onAnimationEnd(animation: Animator?) {
-                rippleInProgress = false
                 onAnimationEnd?.run()
             }
         })
         animator.start()
-        rippleInProgress = true
     }
 
     /** Set the color to be used for the ripple.
      *
      * The alpha value of the color will be applied to the ripple. The alpha range is [0-100].
      */
-    fun setColor(color: Int, alpha: Int = RIPPLE_DEFAULT_ALPHA) {
+    fun setColor(color: Int, alpha: Int = RippleAnimationConfig.RIPPLE_DEFAULT_ALPHA) {
         rippleShader.color = ColorUtils.setAlphaComponent(color, alpha)
     }
 
@@ -137,6 +130,9 @@
         rippleShader.sparkleStrength = strength
     }
 
+    /** Indicates whether the ripple animation is playing. */
+    fun rippleInProgress(): Boolean = animator.isRunning
+
     override fun onDraw(canvas: Canvas?) {
         if (canvas == null || !canvas.isHardwareAccelerated) {
             // Drawing with the ripple shader requires hardware acceleration, so skip
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index e3658de..c8c1337 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -38,6 +38,8 @@
 import androidx.exifinterface.media.ExifInterface;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
@@ -85,10 +87,12 @@
     private final ContentResolver mResolver;
     private CompressFormat mCompressFormat = CompressFormat.PNG;
     private int mQuality = 100;
+    private final FeatureFlags mFlags;
 
     @Inject
-    ImageExporter(ContentResolver resolver) {
+    ImageExporter(ContentResolver resolver, FeatureFlags flags) {
         mResolver = resolver;
+        mFlags = flags;
     }
 
     /**
@@ -161,7 +165,7 @@
             ZonedDateTime captureTime, UserHandle owner) {
 
         final Task task = new Task(mResolver, requestId, bitmap, captureTime, mCompressFormat,
-                mQuality, /* publish */ true, owner);
+                mQuality, /* publish */ true, owner, mFlags);
 
         return CallbackToFutureAdapter.getFuture(
                 (completer) -> {
@@ -209,9 +213,11 @@
         private final UserHandle mOwner;
         private final String mFileName;
         private final boolean mPublish;
+        private final FeatureFlags mFlags;
 
         Task(ContentResolver resolver, UUID requestId, Bitmap bitmap, ZonedDateTime captureTime,
-                CompressFormat format, int quality, boolean publish, UserHandle owner) {
+                CompressFormat format, int quality, boolean publish, UserHandle owner,
+                FeatureFlags flags) {
             mResolver = resolver;
             mRequestId = requestId;
             mBitmap = bitmap;
@@ -221,6 +227,7 @@
             mOwner = owner;
             mFileName = createFilename(mCaptureTime, mFormat);
             mPublish = publish;
+            mFlags = flags;
         }
 
         public Result execute() throws ImageExportException, InterruptedException {
@@ -234,7 +241,7 @@
                     start = Instant.now();
                 }
 
-                uri = createEntry(mResolver, mFormat, mCaptureTime, mFileName, mOwner);
+                uri = createEntry(mResolver, mFormat, mCaptureTime, mFileName, mOwner, mFlags);
                 throwIfInterrupted();
 
                 writeImage(mResolver, mBitmap, mFormat, mQuality, uri);
@@ -278,13 +285,15 @@
     }
 
     private static Uri createEntry(ContentResolver resolver, CompressFormat format,
-            ZonedDateTime time, String fileName, UserHandle owner) throws ImageExportException {
+            ZonedDateTime time, String fileName, UserHandle owner, FeatureFlags flags)
+            throws ImageExportException {
         Trace.beginSection("ImageExporter_createEntry");
         try {
             final ContentValues values = createMetadata(time, format, fileName);
 
             Uri baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
-            if (UserHandle.myUserId() != owner.getIdentifier()) {
+            if (flags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)
+                    && UserHandle.myUserId() != owner.getIdentifier()) {
                 baseUri = ContentProvider.maybeAddUserId(baseUri, owner.getIdentifier());
             }
             Uri uri = resolver.insert(baseUri, values);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index d524a35..9b5295d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -1043,8 +1043,13 @@
     }
 
     private boolean isUserSetupComplete(UserHandle owner) {
-        return Settings.Secure.getInt(mContext.createContextAsUser(owner, 0)
-                .getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+        if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
+            return Settings.Secure.getInt(mContext.createContextAsUser(owner, 0)
+                    .getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+        } else {
+            return Settings.Secure.getInt(mContext.getContentResolver(),
+                    SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index b39175e..760d22ef 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -41,7 +41,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
 import static com.android.systemui.statusbar.VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
@@ -135,7 +134,6 @@
 import com.android.systemui.camera.CameraGestureHelper;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeLog;
@@ -231,7 +229,6 @@
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.LargeScreenUtils;
-import com.android.systemui.util.ListenerSet;
 import com.android.systemui.util.Utils;
 import com.android.systemui.util.time.SystemClock;
 import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -372,7 +369,6 @@
     private final TapAgainViewController mTapAgainViewController;
     private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
     private final RecordingController mRecordingController;
-    private final PanelEventsEmitter mPanelEventsEmitter;
     private final boolean mVibrateOnOpening;
     private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
     private final FlingAnimationUtils mFlingAnimationUtilsClosing;
@@ -880,7 +876,6 @@
             Provider<KeyguardBottomAreaViewController> keyguardBottomAreaViewControllerProvider,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             NotificationListContainer notificationListContainer,
-            PanelEventsEmitter panelEventsEmitter,
             NotificationStackSizeCalculator notificationStackSizeCalculator,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             ShadeTransitionController shadeTransitionController,
@@ -993,7 +988,6 @@
         mMediaDataManager = mediaDataManager;
         mTapAgainViewController = tapAgainViewController;
         mSysUiState = sysUiState;
-        mPanelEventsEmitter = panelEventsEmitter;
         pulseExpansionHandler.setPulseExpandAbortListener(() -> {
             if (mQs != null) {
                 mQs.animateHeaderSlidingOut();
@@ -1948,7 +1942,7 @@
     private void setQsExpandImmediate(boolean expandImmediate) {
         if (expandImmediate != mQsExpandImmediate) {
             mQsExpandImmediate = expandImmediate;
-            mPanelEventsEmitter.notifyExpandImmediateChange(expandImmediate);
+            mShadeExpansionStateManager.notifyExpandImmediateChange(expandImmediate);
         }
     }
 
@@ -1959,7 +1953,7 @@
 
     public void closeQs() {
         cancelQsAnimation();
-        setQsExpansion(mQsMinExpansionHeight);
+        setQsExpansionHeight(mQsMinExpansionHeight);
     }
 
     @VisibleForTesting
@@ -1997,7 +1991,7 @@
             }
             float height = mQsExpansionHeight;
             mQsExpansionAnimator.cancel();
-            setQsExpansion(height);
+            setQsExpansionHeight(height);
         }
         flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE);
     }
@@ -2021,7 +2015,7 @@
             // case but currently motion in portrait looks worse than when using flingSettings.
             // TODO: make below function transitioning smoothly also in portrait with null target
             mLockscreenShadeTransitionController.goToLockedShade(
-                    /* expandedView= */null, /* needsQSAnimation= */false);
+                    /* expandedView= */null, /* needsQSAnimation= */true);
         } else if (isFullyCollapsed()) {
             expand(true /* animate */);
         } else {
@@ -2210,7 +2204,7 @@
                     // Already tracking because onOverscrolled was called. We need to update here
                     // so we don't stop for a frame until the next touch event gets handled in
                     // onTouchEvent.
-                    setQsExpansion(h + mInitialHeightOnTouch);
+                    setQsExpansionHeight(h + mInitialHeightOnTouch);
                     trackMovement(event);
                     return true;
                 } else {
@@ -2621,7 +2615,7 @@
             case MotionEvent.ACTION_MOVE:
                 if (DEBUG_LOGCAT) Log.d(TAG, "onQSTouch move");
                 mShadeLog.logMotionEvent(event, "onQsTouch: move action, setting QS expansion");
-                setQsExpansion(h + mInitialHeightOnTouch);
+                setQsExpansionHeight(h + mInitialHeightOnTouch);
                 if (h >= getFalsingThreshold()) {
                     mQsTouchAboveFalsingThreshold = true;
                 }
@@ -2668,14 +2662,14 @@
 
         // Reset scroll position and apply that position to the expanded height.
         float height = mQsExpansionHeight;
-        setQsExpansion(height);
+        setQsExpansionHeight(height);
         updateExpandedHeightToMaxHeight();
         mNotificationStackScrollLayoutController.checkSnoozeLeavebehind();
 
         // When expanding QS, let's authenticate the user if possible,
         // this will speed up notification actions.
-        if (height == 0) {
-            mCentralSurfaces.requestFaceAuth(false, FaceAuthApiRequestReason.QS_EXPANDED);
+        if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) {
+            mUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.QS_EXPANDED);
         }
     }
 
@@ -2740,7 +2734,7 @@
         mQs.setExpanded(mQsExpanded);
     }
 
-    void setQsExpansion(float height) {
+    void setQsExpansionHeight(float height) {
         height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
         mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
         boolean qsAnimatingAway = !mQsAnimatorExpand && mAnimatingQS;
@@ -3047,11 +3041,23 @@
         // relative to NotificationStackScrollLayout
         int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
         int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
-        int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
+        int nsslTop = getNotificationsClippingTopBounds(top);
         int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
         int bottomRadius = mSplitShadeEnabled ? radius : 0;
+        int topRadius = mSplitShadeEnabled && mExpandingFromHeadsUp ? 0 : radius;
         mNotificationStackScrollLayoutController.setRoundedClippingBounds(
-                nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
+                nsslLeft, nsslTop, nsslRight, nsslBottom, topRadius, bottomRadius);
+    }
+
+    private int getNotificationsClippingTopBounds(int qsTop) {
+        if (mSplitShadeEnabled && mExpandingFromHeadsUp) {
+            // in split shade nssl has extra top margin so clipping at top 0 is not enough, we need
+            // to set top clipping bound to negative value to allow HUN to go up to the top edge of
+            // the screen without clipping.
+            return -mAmbientState.getStackTopMargin();
+        } else {
+            return qsTop - mNotificationStackScrollLayoutController.getTop();
+        }
     }
 
     private float getQSEdgePosition() {
@@ -3162,12 +3168,13 @@
                     delay);
             mIsQsTranslationResetAnimator = mQsTranslationForFullShadeTransition > 0.0f;
         }
-
-        if (mSplitShadeEnabled) {
-            updateQsExpansionForLockscreenToShadeTransition(pxAmount);
-        }
         float endPosition = 0;
         if (pxAmount > 0.0f) {
+            if (mSplitShadeEnabled) {
+                float qsHeight = MathUtils.lerp(mQsMinExpansionHeight, mQsMaxExpansionHeight,
+                        mLockscreenShadeTransitionController.getQSDragProgress());
+                setQsExpansionHeight(qsHeight);
+            }
             if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0
                     && !mMediaDataManager.hasActiveMediaOrRecommendation()) {
                 // No notifications are visible, let's animate to the height of qs instead
@@ -3205,18 +3212,6 @@
         updateQsExpansion();
     }
 
-    private void updateQsExpansionForLockscreenToShadeTransition(float pxAmount) {
-        float qsExpansion = 0;
-        if (pxAmount > 0.0f) {
-            qsExpansion = MathUtils.lerp(mQsMinExpansionHeight, mQsMaxExpansionHeight,
-                    mLockscreenShadeTransitionController.getQSDragProgress());
-        }
-        // SHADE_LOCKED means transition is over and we don't want further updates
-        if (mBarState != SHADE_LOCKED) {
-            setQsExpansion(qsExpansion);
-        }
-    }
-
     /**
      * Notify the panel that the pulse expansion has finished and that we're going to the full
      * shade
@@ -3334,7 +3329,7 @@
             animator.setDuration(350);
         }
         animator.addUpdateListener(
-                animation -> setQsExpansion((Float) animation.getAnimatedValue()));
+                animation -> setQsExpansionHeight((Float) animation.getAnimatedValue()));
         animator.addListener(new AnimatorListenerAdapter() {
             private boolean mIsCanceled;
 
@@ -3455,8 +3450,17 @@
                 positionClockAndNotifications();
             }
         }
-        if (mQsExpandImmediate || (mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
-                && !mQsExpansionFromOverscroll)) {
+        // Below is true when QS are expanded and we swipe up from the same bottom of panel to
+        // close the whole shade with one motion. Also this will be always true when closing
+        // split shade as there QS are always expanded so every collapsing motion is motion from
+        // expanded QS to closed panel
+        boolean collapsingShadeFromExpandedQs = mQsExpanded && !mQsTracking
+                && mQsExpansionAnimator == null && !mQsExpansionFromOverscroll;
+        boolean goingBetweenClosedShadeAndExpandedQs =
+                mQsExpandImmediate || collapsingShadeFromExpandedQs;
+        // we don't want to update QS expansion when HUN is visible because then the whole shade is
+        // initially hidden, even though it has non-zero height
+        if (goingBetweenClosedShadeAndExpandedQs && !mHeadsUpManager.isTrackingHeadsUp()) {
             float qsExpansionFraction;
             if (mSplitShadeEnabled) {
                 qsExpansionFraction = 1;
@@ -3475,7 +3479,7 @@
             }
             float targetHeight = mQsMinExpansionHeight
                     + qsExpansionFraction * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
-            setQsExpansion(targetHeight);
+            setQsExpansionHeight(targetHeight);
         }
         updateExpandedHeight(expandedHeight);
         updateHeader();
@@ -3889,7 +3893,7 @@
         boolean wasRunning = mIsLaunchAnimationRunning;
         mIsLaunchAnimationRunning = running;
         if (wasRunning != mIsLaunchAnimationRunning) {
-            mPanelEventsEmitter.notifyLaunchingActivityChanged(running);
+            mShadeExpansionStateManager.notifyLaunchingActivityChanged(running);
         }
     }
 
@@ -3898,7 +3902,7 @@
         boolean wasClosing = isClosing();
         mClosing = isClosing;
         if (wasClosing != isClosing) {
-            mPanelEventsEmitter.notifyPanelCollapsingChanged(isClosing);
+            mShadeExpansionStateManager.notifyPanelCollapsingChanged(isClosing);
         }
         mAmbientState.setIsClosing(isClosing);
     }
@@ -3933,7 +3937,7 @@
                     mShadeLog.v("onMiddleClicked on Keyguard, mDozingOnDown: false");
                     // Try triggering face auth, this "might" run. Check
                     // KeyguardUpdateMonitor#shouldListenForFace to see when face auth won't run.
-                    boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(true,
+                    boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(
                             FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED);
 
                     if (didFaceAuthRun) {
@@ -4223,8 +4227,8 @@
     /**
      * Sets the dozing state.
      *
-     * @param dozing              {@code true} when dozing.
-     * @param animate             if transition should be animated.
+     * @param dozing  {@code true} when dozing.
+     * @param animate if transition should be animated.
      */
     public void setDozing(boolean dozing, boolean animate) {
         if (dozing == mDozing) return;
@@ -4364,35 +4368,35 @@
     /**
      * Starts fold to AOD animation.
      *
-     * @param startAction invoked when the animation starts.
-     * @param endAction invoked when the animation finishes, also if it was cancelled.
+     * @param startAction  invoked when the animation starts.
+     * @param endAction    invoked when the animation finishes, also if it was cancelled.
      * @param cancelAction invoked when the animation is cancelled, before endAction.
      */
     public void startFoldToAodAnimation(Runnable startAction, Runnable endAction,
             Runnable cancelAction) {
         mView.animate()
-            .translationX(0)
-            .alpha(1f)
-            .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
-            .setInterpolator(EMPHASIZED_DECELERATE)
-            .setListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    startAction.run();
-                }
+                .translationX(0)
+                .alpha(1f)
+                .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
+                .setInterpolator(EMPHASIZED_DECELERATE)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        startAction.run();
+                    }
 
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    cancelAction.run();
-                }
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        cancelAction.run();
+                    }
 
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    endAction.run();
-                }
-            }).setUpdateListener(anim -> {
-                mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
-            }).start();
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        endAction.run();
+                    }
+                }).setUpdateListener(anim -> {
+                    mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
+                }).start();
     }
 
     /**
@@ -4734,8 +4738,6 @@
 
     private void startOpening(MotionEvent event) {
         updatePanelExpansionAndVisibility();
-        // Reset at start so haptic can be triggered as soon as panel starts to open.
-        mHasVibratedOnOpen = false;
         //TODO: keyguard opens QS a different way; log that too?
 
         // Log the position of the swipe that opened the panel
@@ -4752,8 +4754,10 @@
     /**
      * Maybe vibrate as panel is opened.
      *
-     * @param openingWithTouch Whether the panel is being opened with touch. If the panel is instead
-     * being opened programmatically (such as by the open panel gesture), we always play haptic.
+     * @param openingWithTouch Whether the panel is being opened with touch. If the panel is
+     *                         instead
+     *                         being opened programmatically (such as by the open panel gesture), we
+     *                         always play haptic.
      */
     private void maybeVibrateOnOpening(boolean openingWithTouch) {
         if (mVibrateOnOpening) {
@@ -4919,10 +4923,12 @@
         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         animator.addListener(new AnimatorListenerAdapter() {
             private boolean mCancelled;
+
             @Override
             public void onAnimationCancel(Animator animation) {
                 mCancelled = true;
             }
+
             @Override
             public void onAnimationEnd(Animator animation) {
                 mIsSpringBackAnimation = false;
@@ -4970,7 +4976,7 @@
         if (isNaN(h)) {
             Log.wtf(TAG, "ExpandedHeight set to NaN");
         }
-        mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
+        mNotificationShadeWindowController.batchApplyWindowLayoutParams(() -> {
             if (mExpandLatencyTracking && h != 0f) {
                 DejankUtils.postAfterTraversal(
                         () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
@@ -5036,7 +5042,7 @@
     }
 
     public boolean isFullyExpanded() {
-        return mExpandedHeight >= getMaxPanelHeight();
+        return mExpandedHeight >= getMaxPanelTransitionDistance();
     }
 
     public boolean isFullyCollapsed() {
@@ -5161,7 +5167,7 @@
     /**
      * Create an animator that can also overshoot
      *
-     * @param targetHeight the target height
+     * @param targetHeight    the target height
      * @param overshootAmount the amount of overshoot desired
      */
     private ValueAnimator createHeightAnimator(float targetHeight, float overshootAmount) {
@@ -5342,7 +5348,7 @@
             mQsExpansionFromOverscroll = rounded != 0f;
             mLastOverscroll = rounded;
             updateQsState();
-            setQsExpansion(mQsMinExpansionHeight + rounded);
+            setQsExpansionHeight(mQsMinExpansionHeight + rounded);
         }
 
         @Override
@@ -5359,7 +5365,7 @@
                 // make sure we can expand
                 setOverScrolling(false);
             }
-            setQsExpansion(mQsExpansionHeight);
+            setQsExpansionHeight(mQsExpansionHeight);
             boolean canExpand = isQsExpansionEnabled();
             flingSettings(!canExpand && open ? 0f : velocity,
                     open && canExpand ? FLING_EXPAND : FLING_COLLAPSE, () -> {
@@ -5552,7 +5558,7 @@
                 }
             } else {
                 // this else branch means we are doing one of:
-                //  - from KEYGUARD and SHADE (but not expanded shade)
+                //  - from KEYGUARD to SHADE (but not fully expanded as when swiping from the top)
                 //  - from SHADE to KEYGUARD
                 //  - from SHADE_LOCKED to SHADE
                 //  - getting notified again about the current SHADE or KEYGUARD state
@@ -5721,7 +5727,7 @@
                     startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
                 }
             } else if (!mQsExpanded && mQsExpansionAnimator == null) {
-                setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
+                setQsExpansionHeight(mQsMinExpansionHeight + mLastOverscroll);
             } else {
                 mShadeLog.v("onLayoutChange: qs expansion not set");
             }
@@ -5917,49 +5923,11 @@
         }
     }
 
-    @SysUISingleton
-    static class PanelEventsEmitter implements NotifPanelEvents {
-
-        private final ListenerSet<Listener> mListeners = new ListenerSet<>();
-
-        @Inject
-        PanelEventsEmitter() {
-        }
-
-        @Override
-        public void registerListener(@androidx.annotation.NonNull @NonNull Listener listener) {
-            mListeners.addIfAbsent(listener);
-        }
-
-        @Override
-        public void unregisterListener(@androidx.annotation.NonNull @NonNull Listener listener) {
-            mListeners.remove(listener);
-        }
-
-        private void notifyLaunchingActivityChanged(boolean isLaunchingActivity) {
-            for (Listener cb : mListeners) {
-                cb.onLaunchingActivityChanged(isLaunchingActivity);
-            }
-        }
-
-        private void notifyPanelCollapsingChanged(boolean isCollapsing) {
-            for (NotifPanelEvents.Listener cb : mListeners) {
-                cb.onPanelCollapsingChanged(isCollapsing);
-            }
-        }
-
-        private void notifyExpandImmediateChange(boolean expandImmediateEnabled) {
-            for (NotifPanelEvents.Listener cb : mListeners) {
-                cb.onExpandImmediateChanged(expandImmediateEnabled);
-            }
-        }
-    }
-
     /** Handles MotionEvents for the Shade. */
     public final class TouchHandler implements View.OnTouchListener {
         private long mLastTouchDownTime = -1L;
 
-        /** @see ViewGroup#onInterceptTouchEvent(MotionEvent)  */
+        /** @see ViewGroup#onInterceptTouchEvent(MotionEvent) */
         public boolean onInterceptTouchEvent(MotionEvent event) {
             if (SPEW_LOGCAT) {
                 Log.v(TAG,
@@ -6158,7 +6126,7 @@
                 mShadeLog.logMotionEvent(event, "onTouch: touch ignored due to instant expanding");
                 return false;
             }
-            if (mTouchDisabled  && event.getActionMasked() != MotionEvent.ACTION_CANCEL) {
+            if (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL) {
                 mShadeLog.logMotionEvent(event, "onTouch: non-cancel action, touch disabled");
                 return false;
             }
@@ -6253,6 +6221,10 @@
                     }
                     break;
                 case MotionEvent.ACTION_MOVE:
+                    if (isFullyCollapsed()) {
+                        // If panel is fully collapsed, reset haptic effect before adding movement.
+                        mHasVibratedOnOpen = false;
+                    }
                     addMovement(event);
                     if (!isFullyCollapsed()) {
                         maybeVibrateOnOpening(true /* openingWithTouch */);
@@ -6366,3 +6338,4 @@
         }
     }
 }
+
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEventsModule.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java
similarity index 77%
rename from packages/SystemUI/src/com/android/systemui/shade/NotifPanelEventsModule.java
rename to packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java
index 6772384..959c339 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEventsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEventsModule.java
@@ -21,10 +21,9 @@
 import dagger.Binds;
 import dagger.Module;
 
-/** Provides a {@link NotifPanelEvents} in {@link SysUISingleton} scope. */
+/** Provides a {@link ShadeStateEvents} in {@link SysUISingleton} scope. */
 @Module
-public abstract class NotifPanelEventsModule {
+public abstract class ShadeEventsModule {
     @Binds
-    abstract NotifPanelEvents bindPanelEvents(
-            NotificationPanelViewController.PanelEventsEmitter impl);
+    abstract ShadeStateEvents bindShadeEvents(ShadeExpansionStateManager impl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 7bba74a..667392c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -20,6 +20,7 @@
 import android.util.Log
 import androidx.annotation.FloatRange
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.ShadeStateEvents.ShadeStateEventsListener
 import com.android.systemui.util.Compile
 import java.util.concurrent.CopyOnWriteArrayList
 import javax.inject.Inject
@@ -30,11 +31,12 @@
  * TODO(b/200063118): Make this class the one source of truth for the state of panel expansion.
  */
 @SysUISingleton
-class ShadeExpansionStateManager @Inject constructor() {
+class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents {
 
     private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>()
     private val qsExpansionListeners = CopyOnWriteArrayList<ShadeQsExpansionListener>()
     private val stateListeners = CopyOnWriteArrayList<ShadeStateListener>()
+    private val shadeStateEventsListeners = CopyOnWriteArrayList<ShadeStateEventsListener>()
 
     @PanelState private var state: Int = STATE_CLOSED
     @FloatRange(from = 0.0, to = 1.0) private var fraction: Float = 0f
@@ -79,6 +81,14 @@
         stateListeners.remove(listener)
     }
 
+    override fun addShadeStateEventsListener(listener: ShadeStateEventsListener) {
+        shadeStateEventsListeners.addIfAbsent(listener)
+    }
+
+    override fun removeShadeStateEventsListener(listener: ShadeStateEventsListener) {
+        shadeStateEventsListeners.remove(listener)
+    }
+
     /** Returns true if the panel is currently closed and false otherwise. */
     fun isClosed(): Boolean = state == STATE_CLOSED
 
@@ -162,6 +172,24 @@
         stateListeners.forEach { it.onPanelStateChanged(state) }
     }
 
+    fun notifyLaunchingActivityChanged(isLaunchingActivity: Boolean) {
+        for (cb in shadeStateEventsListeners) {
+            cb.onLaunchingActivityChanged(isLaunchingActivity)
+        }
+    }
+
+    fun notifyPanelCollapsingChanged(isCollapsing: Boolean) {
+        for (cb in shadeStateEventsListeners) {
+            cb.onPanelCollapsingChanged(isCollapsing)
+        }
+    }
+
+    fun notifyExpandImmediateChange(expandImmediateEnabled: Boolean) {
+        for (cb in shadeStateEventsListeners) {
+            cb.onExpandImmediateChanged(expandImmediateEnabled)
+        }
+    }
+
     private fun debugLog(msg: String) {
         if (!DEBUG) return
         Log.v(TAG, msg)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt
rename to packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
index 4558061..56bb1a6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
@@ -16,27 +16,25 @@
 
 package com.android.systemui.shade
 
-/** Provides certain notification panel events.  */
-interface NotifPanelEvents {
+/** Provides certain notification panel events. */
+interface ShadeStateEvents {
 
-    /** Registers callbacks to be invoked when notification panel events occur.  */
-    fun registerListener(listener: Listener)
+    /** Registers callbacks to be invoked when notification panel events occur. */
+    fun addShadeStateEventsListener(listener: ShadeStateEventsListener)
 
-    /** Unregisters callbacks previously registered via [registerListener]  */
-    fun unregisterListener(listener: Listener)
+    /** Unregisters callbacks previously registered via [addShadeStateEventsListener] */
+    fun removeShadeStateEventsListener(listener: ShadeStateEventsListener)
 
     /** Callbacks for certain notification panel events. */
-    interface Listener {
+    interface ShadeStateEventsListener {
 
         /** Invoked when the notification panel starts or stops collapsing. */
-        @JvmDefault
-        fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
+        @JvmDefault fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
 
         /**
          * Invoked when the notification panel starts or stops launching an [android.app.Activity].
          */
-        @JvmDefault
-        fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
+        @JvmDefault fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
 
         /**
          * Invoked when the "expand immediate" attribute changes.
@@ -47,7 +45,6 @@
          * Another example is when full QS is showing, and we swipe up from the bottom. Instead of
          * going to QQS, the panel fully collapses.
          */
-        @JvmDefault
-        fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
+        @JvmDefault fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index fc984618..5873837 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -48,7 +48,8 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.regionsampling.RegionSamplingInstance
+import com.android.systemui.shared.regionsampling.RegionSampler
+import com.android.systemui.shared.regionsampling.UpdateColorCallback
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -90,8 +91,8 @@
 
     // Smartspace can be used on multiple displays, such as when the user casts their screen
     private var smartspaceViews = mutableSetOf<SmartspaceView>()
-    private var regionSamplingInstances =
-            mutableMapOf<SmartspaceView, RegionSamplingInstance>()
+    private var regionSamplers =
+            mutableMapOf<SmartspaceView, RegionSampler>()
 
     private val regionSamplingEnabled =
             featureFlags.isEnabled(Flags.REGION_SAMPLING)
@@ -101,27 +102,23 @@
     private var showSensitiveContentForManagedUser = false
     private var managedUserHandle: UserHandle? = null
 
-    private val updateFun = object : RegionSamplingInstance.UpdateColorCallback {
-        override fun updateColors() {
-            updateTextColorFromRegionSampler()
-        }
-    }
+    private val updateFun: UpdateColorCallback = { updateTextColorFromRegionSampler() }
 
     // TODO: Move logic into SmartspaceView
     var stateChangeListener = object : View.OnAttachStateChangeListener {
         override fun onViewAttachedToWindow(v: View) {
             smartspaceViews.add(v as SmartspaceView)
 
-            var regionSamplingInstance = RegionSamplingInstance(
+            var regionSampler = RegionSampler(
                     v,
                     uiExecutor,
                     bgExecutor,
                     regionSamplingEnabled,
                     updateFun
             )
-            initializeTextColors(regionSamplingInstance)
-            regionSamplingInstance.startRegionSampler()
-            regionSamplingInstances.put(v, regionSamplingInstance)
+            initializeTextColors(regionSampler)
+            regionSampler.startRegionSampler()
+            regionSamplers.put(v, regionSampler)
             connectSession()
 
             updateTextColorFromWallpaper()
@@ -131,9 +128,9 @@
         override fun onViewDetachedFromWindow(v: View) {
             smartspaceViews.remove(v as SmartspaceView)
 
-            var regionSamplingInstance = regionSamplingInstances.getValue(v)
-            regionSamplingInstance.stopRegionSampler()
-            regionSamplingInstances.remove(v)
+            var regionSampler = regionSamplers.getValue(v)
+            regionSampler.stopRegionSampler()
+            regionSamplers.remove(v)
 
             if (smartspaceViews.isEmpty()) {
                 disconnect()
@@ -363,19 +360,19 @@
         }
     }
 
-    private fun initializeTextColors(regionSamplingInstance: RegionSamplingInstance) {
+    private fun initializeTextColors(regionSampler: RegionSampler) {
         val lightThemeContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_LightWallpaper)
         val darkColor = Utils.getColorAttrDefaultColor(lightThemeContext, R.attr.wallpaperTextColor)
 
         val darkThemeContext = ContextThemeWrapper(context, R.style.Theme_SystemUI)
         val lightColor = Utils.getColorAttrDefaultColor(darkThemeContext, R.attr.wallpaperTextColor)
 
-        regionSamplingInstance.setForegroundColors(lightColor, darkColor)
+        regionSampler.setForegroundColors(lightColor, darkColor)
     }
 
     private fun updateTextColorFromRegionSampler() {
         smartspaceViews.forEach {
-            val textColor = regionSamplingInstances.getValue(it).currentForegroundColor()
+            val textColor = regionSamplers.getValue(it).currentForegroundColor()
             it.setPrimaryTextColor(textColor)
         }
     }
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 d3bc257..3002a68 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
@@ -28,7 +28,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotifPanelEvents;
+import com.android.systemui.shade.ShadeStateEvents;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -55,12 +55,12 @@
 // TODO(b/204468557): Move to @CoordinatorScope
 @SysUISingleton
 public class VisualStabilityCoordinator implements Coordinator, Dumpable,
-        NotifPanelEvents.Listener {
+        ShadeStateEvents.ShadeStateEventsListener {
     public static final String TAG = "VisualStability";
     public static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
     private final DelayableExecutor mDelayableExecutor;
     private final HeadsUpManager mHeadsUpManager;
-    private final NotifPanelEvents mNotifPanelEvents;
+    private final ShadeStateEvents mShadeStateEvents;
     private final StatusBarStateController mStatusBarStateController;
     private final VisualStabilityProvider mVisualStabilityProvider;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
@@ -92,7 +92,7 @@
             DelayableExecutor delayableExecutor,
             DumpManager dumpManager,
             HeadsUpManager headsUpManager,
-            NotifPanelEvents notifPanelEvents,
+            ShadeStateEvents shadeStateEvents,
             StatusBarStateController statusBarStateController,
             VisualStabilityProvider visualStabilityProvider,
             WakefulnessLifecycle wakefulnessLifecycle) {
@@ -101,7 +101,7 @@
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mStatusBarStateController = statusBarStateController;
         mDelayableExecutor = delayableExecutor;
-        mNotifPanelEvents = notifPanelEvents;
+        mShadeStateEvents = shadeStateEvents;
 
         dumpManager.registerDumpable(this);
     }
@@ -114,7 +114,7 @@
 
         mStatusBarStateController.addCallback(mStatusBarStateControllerListener);
         mPulsing = mStatusBarStateController.isPulsing();
-        mNotifPanelEvents.registerListener(this);
+        mShadeStateEvents.addShadeStateEventsListener(this);
 
         pipeline.setVisualStabilityManager(mNotifStabilityManager);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index da4cced..ff63891 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -32,8 +32,8 @@
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.shade.NotifPanelEventsModule;
 import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.ShadeEventsModule;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
@@ -93,7 +93,7 @@
 @Module(includes = {
         CoordinatorsModule.class,
         KeyguardNotificationVisibilityProviderModule.class,
-        NotifPanelEventsModule.class,
+        ShadeEventsModule.class,
         NotifPipelineChoreographerModule.class,
         NotificationSectionHeadersModule.class,
 })
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 df705c5..c4ef28e 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
@@ -1383,7 +1383,7 @@
             if (height < minExpansionHeight) {
                 mClipRect.left = 0;
                 mClipRect.right = getWidth();
-                mClipRect.top = 0;
+                mClipRect.top = getNotificationsClippingTopBound();
                 mClipRect.bottom = (int) height;
                 height = minExpansionHeight;
                 setRequestedClipBounds(mClipRect);
@@ -1444,6 +1444,17 @@
         notifyAppearChangedListeners();
     }
 
+    private int getNotificationsClippingTopBound() {
+        if (isHeadsUpTransition()) {
+            // HUN in split shade can go higher than bottom of NSSL when swiping up so we want
+            // to give it extra clipping margin. Because clipping has rounded corners, we also
+            // need to account for that corner clipping.
+            return -mAmbientState.getStackTopMargin() - mCornerRadius;
+        } else {
+            return 0;
+        }
+    }
+
     private void notifyAppearChangedListeners() {
         float appear;
         float expandAmount;
@@ -1482,7 +1493,6 @@
     public void updateClipping() {
         boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
                 && !mHeadsUpAnimatingAway;
-        boolean clipToOutline = false;
         if (mIsClipped != clipped) {
             mIsClipped = clipped;
         }
@@ -1498,7 +1508,7 @@
             setClipBounds(null);
         }
 
-        setClipToOutline(clipToOutline);
+        setClipToOutline(false);
     }
 
     /**
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 70cf56d..2504fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -41,7 +41,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.RegisterStatusBarResult;
 import com.android.keyguard.AuthKeyguardMessageArea;
-import com.android.keyguard.FaceAuthApiRequestReason;
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.RemoteTransitionAdapter;
@@ -230,13 +229,6 @@
 
     boolean isShadeDisabled();
 
-    /**
-     * Request face auth to initiated
-     * @param userInitiatedRequest Whether this was a user initiated request
-     * @param reason Reason why face auth was triggered.
-     */
-    void requestFaceAuth(boolean userInitiatedRequest, @FaceAuthApiRequestReason String reason);
-
     @Override
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
             int flags);
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 43bc99f..d227ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -122,7 +122,6 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.RegisterStatusBarResult;
 import com.android.keyguard.AuthKeyguardMessageArea;
-import com.android.keyguard.FaceAuthApiRequestReason;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
@@ -1614,18 +1613,6 @@
         return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
     }
 
-    /**
-     * Asks {@link KeyguardUpdateMonitor} to run face auth.
-     */
-    @Override
-    public void requestFaceAuth(boolean userInitiatedRequest,
-            @FaceAuthApiRequestReason String reason) {
-        if (!mKeyguardStateController.canDismissLockScreen()) {
-            mKeyguardUpdateMonitor.requestFaceAuth(
-                    userInitiatedRequest, reason);
-        }
-    }
-
     private void updateReportRejectedTouchVisibility() {
         if (mReportRejectedTouch == null) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index 5e26cf0..4550cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -73,7 +73,6 @@
             isListening = false
             updateListeningState()
             keyguardUpdateMonitor.requestFaceAuth(
-                true,
                 FaceAuthApiRequestReason.PICK_UP_GESTURE_TRIGGERED
             )
             keyguardUpdateMonitor.requestActiveUnlock(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 9767103..c189ace 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -649,37 +649,6 @@
         return mNumDots > 0;
     }
 
-    /**
-     * If the overflow is in the range [1, max_dots - 1) (basically 1 or 2 dots), then
-     * extra padding will have to be accounted for
-     *
-     * This method has no meaning for non-static containers
-     */
-    public boolean hasPartialOverflow() {
-        return mNumDots > 0 && mNumDots < MAX_DOTS;
-    }
-
-    /**
-     * Get padding that can account for extra dots up to the max. The only valid values for
-     * this method are for 1 or 2 dots.
-     * @return only extraDotPadding or extraDotPadding * 2
-     */
-    public int getPartialOverflowExtraPadding() {
-        if (!hasPartialOverflow()) {
-            return 0;
-        }
-
-        int partialOverflowAmount = (MAX_DOTS - mNumDots) * (mStaticDotDiameter + mDotPadding);
-
-        int adjustedWidth = getFinalTranslationX() + partialOverflowAmount;
-        // In case we actually give too much padding...
-        if (adjustedWidth > getWidth()) {
-            partialOverflowAmount = getWidth() - getFinalTranslationX();
-        }
-
-        return partialOverflowAmount;
-    }
-
     // Give some extra room for btw notifications if we can
     public int getNoOverflowExtraPadding() {
         if (mNumDots != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt
new file mode 100644
index 0000000..da87f73
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.statusbar.pipeline.mobile.data.model
+
+import android.telephony.TelephonyManager.DATA_CONNECTED
+import android.telephony.TelephonyManager.DATA_CONNECTING
+import android.telephony.TelephonyManager.DATA_DISCONNECTED
+import android.telephony.TelephonyManager.DATA_DISCONNECTING
+import android.telephony.TelephonyManager.DataState
+
+/** Internal enum representation of the telephony data connection states */
+enum class DataConnectionState(@DataState val dataState: Int) {
+    Connected(DATA_CONNECTED),
+    Connecting(DATA_CONNECTING),
+    Disconnected(DATA_DISCONNECTED),
+    Disconnecting(DATA_DISCONNECTING),
+}
+
+fun @receiver:DataState Int.toDataConnectionType(): DataConnectionState =
+    when (this) {
+        DATA_CONNECTED -> DataConnectionState.Connected
+        DATA_CONNECTING -> DataConnectionState.Connecting
+        DATA_DISCONNECTED -> DataConnectionState.Disconnected
+        DATA_DISCONNECTING -> DataConnectionState.Disconnecting
+        else -> throw IllegalArgumentException("unknown data state received")
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileSubscriptionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileSubscriptionModel.kt
index eaba0e9..6341a11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileSubscriptionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileSubscriptionModel.kt
@@ -28,6 +28,7 @@
 import android.telephony.TelephonyDisplayInfo
 import android.telephony.TelephonyManager
 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Disconnected
 
 /**
  * Data class containing all of the relevant information for a particular line of service, known as
@@ -49,14 +50,14 @@
     @IntRange(from = 0, to = 4)
     val primaryLevel: Int = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
 
-    /** Comes directly from [DataConnectionStateListener.onDataConnectionStateChanged] */
-    val dataConnectionState: Int? = null,
+    /** Mapped from [DataConnectionStateListener.onDataConnectionStateChanged] */
+    val dataConnectionState: DataConnectionState = Disconnected,
 
     /** From [DataActivityListener.onDataActivity]. See [TelephonyManager] for the values */
     @DataActivityType val dataActivityDirection: Int? = null,
 
     /** From [CarrierNetworkListener.onCarrierNetworkChange] */
-    val carrierNetworkChangeActive: Boolean? = null,
+    val carrierNetworkChangeActive: Boolean = false,
 
     /**
      * From [DisplayInfoListener.onDisplayInfoChanged].
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 45284cf..06e8f46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -31,7 +31,9 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DefaultNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.toDataConnectionType
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
 import java.lang.IllegalStateException
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
@@ -42,7 +44,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -62,13 +64,15 @@
      * listener + model.
      */
     val subscriptionModelFlow: Flow<MobileSubscriptionModel>
+    /** Observable tracking [TelephonyManager.isDataConnectionAllowed] */
+    val dataEnabled: Flow<Boolean>
 }
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @OptIn(ExperimentalCoroutinesApi::class)
 class MobileConnectionRepositoryImpl(
     private val subId: Int,
-    telephonyManager: TelephonyManager,
+    private val telephonyManager: TelephonyManager,
     bgDispatcher: CoroutineDispatcher,
     logger: ConnectivityPipelineLogger,
     scope: CoroutineScope,
@@ -127,7 +131,8 @@
                             dataState: Int,
                             networkType: Int
                         ) {
-                            state = state.copy(dataConnectionState = dataState)
+                            state =
+                                state.copy(dataConnectionState = dataState.toDataConnectionType())
                             trySend(state)
                         }
 
@@ -160,10 +165,21 @@
                 telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
                 awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
             }
-            .onEach { logger.logOutputChange("mobileSubscriptionModel", it.toString()) }
+            .logOutputChange(logger, "MobileSubscriptionModel")
             .stateIn(scope, SharingStarted.WhileSubscribed(), state)
     }
 
+    /**
+     * There are a few cases where we will need to poll [TelephonyManager] so we can update some
+     * internal state where callbacks aren't provided. Any of those events should be merged into
+     * this flow, which can be used to trigger the polling.
+     */
+    private val telephonyPollingEvent: Flow<Unit> = subscriptionModelFlow.map {}
+
+    override val dataEnabled: Flow<Boolean> = telephonyPollingEvent.map { dataConnectionAllowed() }
+
+    private fun dataConnectionAllowed(): Boolean = telephonyManager.isDataConnectionAllowed
+
     class Factory
     @Inject
     constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 15f4acc..f99d278c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -29,6 +29,9 @@
 import kotlinx.coroutines.flow.map
 
 interface MobileIconInteractor {
+    /** Observable for the data enabled state of this connection */
+    val isDataEnabled: Flow<Boolean>
+
     /** Observable for RAT type (network type) indicator */
     val networkTypeIconGroup: Flow<MobileIconGroup>
 
@@ -54,6 +57,8 @@
 ) : MobileIconInteractor {
     private val mobileStatusInfo = connectionRepository.subscriptionModelFlow
 
+    override val isDataEnabled: Flow<Boolean> = connectionRepository.dataEnabled
+
     /** Observable for the current RAT indicator icon ([MobileIconGroup]) */
     override val networkTypeIconGroup: Flow<MobileIconGroup> =
         combine(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index cd411a4..614d583 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -23,12 +23,14 @@
 import com.android.settingslib.mobile.TelephonyIcons
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
 import com.android.systemui.util.CarrierConfigTracker
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -47,28 +49,38 @@
  * icon
  */
 interface MobileIconsInteractor {
+    /** List of subscriptions, potentially filtered for CBRS */
     val filteredSubscriptions: Flow<List<SubscriptionInfo>>
+    /** The icon mapping from network type to [MobileIconGroup] for the default subscription */
     val defaultMobileIconMapping: Flow<Map<String, MobileIconGroup>>
+    /** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
     val defaultMobileIconGroup: Flow<MobileIconGroup>
+    /** True once the user has been set up */
     val isUserSetup: Flow<Boolean>
+    /**
+     * Vends out a [MobileIconInteractor] tracking the [MobileConnectionRepository] for the given
+     * subId. Will throw if the ID is invalid
+     */
     fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor
 }
 
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MobileIconsInteractorImpl
 @Inject
 constructor(
-    private val mobileSubscriptionRepo: MobileConnectionsRepository,
+    private val mobileConnectionsRepo: MobileConnectionsRepository,
     private val carrierConfigTracker: CarrierConfigTracker,
     private val mobileMappingsProxy: MobileMappingsProxy,
     userSetupRepo: UserSetupRepository,
     @Application private val scope: CoroutineScope,
 ) : MobileIconsInteractor {
     private val activeMobileDataSubscriptionId =
-        mobileSubscriptionRepo.activeMobileDataSubscriptionId
+        mobileConnectionsRepo.activeMobileDataSubscriptionId
 
     private val unfilteredSubscriptions: Flow<List<SubscriptionInfo>> =
-        mobileSubscriptionRepo.subscriptionsFlow
+        mobileConnectionsRepo.subscriptionsFlow
 
     /**
      * Generally, SystemUI wants to show iconography for each subscription that is listed by
@@ -119,13 +131,13 @@
      * subscription Id. This mapping is the same for every subscription.
      */
     override val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>> =
-        mobileSubscriptionRepo.defaultDataSubRatConfig
+        mobileConnectionsRepo.defaultDataSubRatConfig
             .map { mobileMappingsProxy.mapIconSets(it) }
             .stateIn(scope, SharingStarted.WhileSubscribed(), initialValue = mapOf())
 
     /** If there is no mapping in [defaultMobileIconMapping], then use this default icon group */
     override val defaultMobileIconGroup: StateFlow<MobileIconGroup> =
-        mobileSubscriptionRepo.defaultDataSubRatConfig
+        mobileConnectionsRepo.defaultDataSubRatConfig
             .map { mobileMappingsProxy.getDefaultIcons(it) }
             .stateIn(scope, SharingStarted.WhileSubscribed(), initialValue = TelephonyIcons.G)
 
@@ -137,6 +149,6 @@
             defaultMobileIconMapping,
             defaultMobileIconGroup,
             mobileMappingsProxy,
-            mobileSubscriptionRepo.getRepoForSubId(subId),
+            mobileConnectionsRepo.getRepoForSubId(subId),
         )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index cc8f6dd..8131739 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -28,7 +28,6 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
 
 /**
  * View model for the state of a single mobile icon. Each [MobileIconViewModel] will keep watch over
@@ -59,12 +58,18 @@
 
     /** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
     var networkTypeIcon: Flow<Icon?> =
-        iconInteractor.networkTypeIconGroup.map {
-            val desc =
-                if (it.dataContentDescription != 0)
-                    ContentDescription.Resource(it.dataContentDescription)
-                else null
-            Icon.Resource(it.dataType, desc)
+        combine(iconInteractor.networkTypeIconGroup, iconInteractor.isDataEnabled) {
+            networkTypeIconGroup,
+            isDataEnabled ->
+            if (!isDataEnabled) {
+                null
+            } else {
+                val desc =
+                    if (networkTypeIconGroup.dataContentDescription != 0)
+                        ContentDescription.Resource(networkTypeIconGroup.dataContentDescription)
+                    else null
+                Icon.Resource(networkTypeIconGroup.dataType, desc)
+            }
         }
 
     var tint: Flow<Int> = flowOf(Color.CYAN)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
index 118b94c7..6efb10f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
@@ -34,7 +34,7 @@
 @Inject
 constructor(dumpManager: DumpManager, telephonyManager: TelephonyManager) : Dumpable {
     init {
-        dumpManager.registerDumpable("$SB_LOGGING_TAG:ConnectivityConstants", this)
+        dumpManager.registerDumpable("${SB_LOGGING_TAG}Constants", this)
     }
 
     /** True if this device has the capability for data connections and false otherwise. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
index 6b1750d..45c6d46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
@@ -62,7 +62,7 @@
     tunerService: TunerService,
 ) : ConnectivityRepository, Dumpable {
     init {
-        dumpManager.registerDumpable("$SB_LOGGING_TAG:ConnectivityRepository", this)
+        dumpManager.registerDumpable("${SB_LOGGING_TAG}Repository", this)
     }
 
     // The default set of hidden icons to use if we don't get any from [TunerService].
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
index 0eb4b0d..3c0eb91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
@@ -35,7 +35,7 @@
         dumpManager: DumpManager,
 ) : Dumpable {
     init {
-        dumpManager.registerDumpable("$SB_LOGGING_TAG:WifiConstants", this)
+        dumpManager.registerDumpable("${SB_LOGGING_TAG}WifiConstants", this)
     }
 
     /** True if we should show the activityIn/activityOut icons and false otherwise. */
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index f0a50de..637fac0 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -44,11 +44,6 @@
  *
  * The generic type T is expected to contain all the information necessary for the subclasses to
  * display the view in a certain state, since they receive <T> in [updateView].
- *
- * @property windowTitle the title to use for the window that displays the temporary view. Should be
- *   normally cased, like "Window Title".
- * @property wakeReason a string used for logging if we needed to wake the screen in order to
- *   display the temporary view. Should be screaming snake cased, like WAKE_REASON.
  */
 abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : TemporaryViewLogger>(
     internal val context: Context,
@@ -59,8 +54,6 @@
     private val configurationController: ConfigurationController,
     private val powerManager: PowerManager,
     @LayoutRes private val viewLayoutRes: Int,
-    private val windowTitle: String,
-    private val wakeReason: String,
 ) : CoreStartable {
     /**
      * Window layout params that will be used as a starting point for the [windowLayoutParams] of
@@ -72,7 +65,6 @@
         type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
         flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
             WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-        title = windowTitle
         format = PixelFormat.TRANSLUCENT
         setTrustedOverlay()
     }
@@ -100,29 +92,40 @@
     fun displayView(newInfo: T) {
         val currentDisplayInfo = displayInfo
 
-        if (currentDisplayInfo != null) {
+        if (currentDisplayInfo != null &&
+            currentDisplayInfo.info.windowTitle == newInfo.windowTitle) {
+            // We're already displaying information in the correctly-titled window, so we just need
+            // to update the view.
             currentDisplayInfo.info = newInfo
             updateView(currentDisplayInfo.info, currentDisplayInfo.view)
         } else {
-            // The view is new, so set up all our callbacks and inflate the view
+            if (currentDisplayInfo != null) {
+                // We're already displaying information but that information is under a different
+                // window title. So, we need to remove the old window with the old title and add a
+                // new window with the new title.
+                removeView(removalReason = "New info has new window title: ${newInfo.windowTitle}")
+            }
+
+            // At this point, we're guaranteed to no longer be displaying a view.
+            // So, set up all our callbacks and inflate the view.
             configurationController.addCallback(displayScaleListener)
             // Wake the screen if necessary so the user will see the view. (Per b/239426653, we want
             // the view to show over the dream state, so we should only wake up if the screen is
             // completely off.)
             if (!powerManager.isScreenOn) {
                 powerManager.wakeUp(
-                        SystemClock.uptimeMillis(),
-                        PowerManager.WAKE_REASON_APPLICATION,
-                        "com.android.systemui:$wakeReason",
+                    SystemClock.uptimeMillis(),
+                    PowerManager.WAKE_REASON_APPLICATION,
+                    "com.android.systemui:${newInfo.wakeReason}",
                 )
             }
-            logger.logChipAddition()
+            logger.logViewAddition(newInfo.windowTitle)
             inflateAndUpdateView(newInfo)
         }
 
         // Cancel and re-set the view timeout each time we get a new state.
         val timeout = accessibilityManager.getRecommendedTimeoutMillis(
-            newInfo.getTimeoutMs().toInt(),
+            newInfo.timeoutMs,
             // Not all views have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
             // include it just to be safe.
             FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS
@@ -147,7 +150,12 @@
         val newDisplayInfo = DisplayInfo(newView, newInfo)
         displayInfo = newDisplayInfo
         updateView(newDisplayInfo.info, newDisplayInfo.view)
-        windowManager.addView(newView, windowLayoutParams)
+
+        val paramsWithTitle = WindowManager.LayoutParams().also {
+            it.copyFrom(windowLayoutParams)
+            it.title = newInfo.windowTitle
+        }
+        windowManager.addView(newView, paramsWithTitle)
         animateViewIn(newView)
     }
 
@@ -177,7 +185,7 @@
         val currentView = currentDisplayInfo.view
         animateViewOut(currentView) { windowManager.removeView(currentView) }
 
-        logger.logChipRemoval(removalReason)
+        logger.logViewRemoval(removalReason)
         configurationController.removeCallback(displayScaleListener)
         // Re-set to null immediately (instead as part of the animation end runnable) so
         // that if a new view event comes in while this view is animating out, we still display the
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
index 4fe753a..cbb5002 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
@@ -19,12 +19,24 @@
 /**
  * A superclass view state used with [TemporaryViewDisplayController].
  */
-interface TemporaryViewInfo {
+abstract class TemporaryViewInfo {
     /**
-     * Returns the amount of time the given view state should display on the screen before it times
-     * out and disappears.
+     * The title to use for the window that displays the temporary view. Should be normally cased,
+     * like "Window Title".
      */
-    fun getTimeoutMs(): Long = DEFAULT_TIMEOUT_MILLIS
+    abstract val windowTitle: String
+
+    /**
+     * A string used for logging if we needed to wake the screen in order to display the temporary
+     * view. Should be screaming snake cased, like WAKE_REASON.
+     */
+    abstract val wakeReason: String
+
+    /**
+     * The amount of time the given view state should display on the screen before it times out and
+     * disappears.
+     */
+    open val timeoutMs: Int = DEFAULT_TIMEOUT_MILLIS
 }
 
-const val DEFAULT_TIMEOUT_MILLIS = 10000L
+const val DEFAULT_TIMEOUT_MILLIS = 10000
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
index a7185cb..428a104 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
@@ -24,13 +24,13 @@
     internal val buffer: LogBuffer,
     internal val tag: String,
 ) {
-    /** Logs that we added the chip to a new window. */
-    fun logChipAddition() {
-        buffer.log(tag, LogLevel.DEBUG, {}, { "Chip added" })
+    /** Logs that we added the view in a window titled [windowTitle]. */
+    fun logViewAddition(windowTitle: String) {
+        buffer.log(tag, LogLevel.DEBUG, { str1 = windowTitle }, { "View added. window=$str1" })
     }
 
     /** Logs that we removed the chip for the given [reason]. */
-    fun logChipRemoval(reason: String) {
-        buffer.log(tag, LogLevel.DEBUG, { str1 = reason }, { "Chip removed due to $str1" })
+    fun logViewRemoval(reason: String) {
+        buffer.log(tag, LogLevel.DEBUG, { str1 = reason }, { "View removed due to: $str1" })
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index b8930a4..87b6e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -38,9 +38,6 @@
 import com.android.systemui.common.ui.binder.TextViewBinder
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
-import com.android.systemui.media.taptotransfer.common.MediaTttUtils
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -64,14 +61,11 @@
  * Only one chipbar may be shown at a time.
  * TODO(b/245610654): Should we just display whichever chipbar was most recently requested, or do we
  *   need to maintain a priority ordering?
- *
- * TODO(b/245610654): Remove all media-related items from this class so it's just for generic
- *   chipbars.
  */
 @SysUISingleton
 open class ChipbarCoordinator @Inject constructor(
         context: Context,
-        @MediaTttSenderLogger logger: MediaTttLogger,
+        logger: ChipbarLogger,
         windowManager: WindowManager,
         @Main mainExecutor: DelayableExecutor,
         accessibilityManager: AccessibilityManager,
@@ -81,7 +75,7 @@
         private val falsingCollector: FalsingCollector,
         private val viewUtil: ViewUtil,
         private val vibratorHelper: VibratorHelper,
-) : TemporaryViewDisplayController<ChipbarInfo, MediaTttLogger>(
+) : TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>(
         context,
         logger,
         windowManager,
@@ -90,8 +84,6 @@
         configurationController,
         powerManager,
         R.layout.chipbar,
-        MediaTttUtils.WINDOW_TITLE,
-        MediaTttUtils.WAKE_REASON,
 ) {
 
     private lateinit var parent: ChipbarRootView
@@ -106,7 +98,16 @@
         newInfo: ChipbarInfo,
         currentView: ViewGroup
     ) {
-        // TODO(b/245610654): Adding logging here.
+        logger.logViewUpdate(
+            newInfo.windowTitle,
+            newInfo.text.loadText(context),
+            when (newInfo.endItem) {
+                null -> "null"
+                is ChipbarEndItem.Loading -> "loading"
+                is ChipbarEndItem.Error -> "error"
+                is ChipbarEndItem.Button -> "button(${newInfo.endItem.text.loadText(context)})"
+            }
+        )
 
         // Detect falsing touches on the chip.
         parent = currentView.requireViewById(R.id.chipbar_root_view)
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 57fde87..6237365 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -37,7 +37,10 @@
     val text: Text,
     val endItem: ChipbarEndItem?,
     val vibrationEffect: VibrationEffect? = null,
-) : TemporaryViewInfo
+    override val windowTitle: String,
+    override val wakeReason: String,
+    override val timeoutMs: Int,
+) : TemporaryViewInfo()
 
 /** The possible items to display at the end of the chipbar. */
 sealed class ChipbarEndItem {
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
new file mode 100644
index 0000000..e477cd6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.temporarydisplay.chipbar
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.temporarydisplay.TemporaryViewLogger
+import com.android.systemui.temporarydisplay.dagger.ChipbarLog
+import javax.inject.Inject
+
+/** A logger for the chipbar. */
+@SysUISingleton
+class ChipbarLogger
+@Inject
+constructor(
+    @ChipbarLog buffer: LogBuffer,
+) : TemporaryViewLogger(buffer, "ChipbarLog") {
+    /**
+     * Logs that the chipbar was updated to display in a window named [windowTitle], with [text] and
+     * [endItemDesc].
+     */
+    fun logViewUpdate(windowTitle: String, text: String?, endItemDesc: String) {
+        buffer.log(
+            tag,
+            LogLevel.DEBUG,
+            {
+                str1 = windowTitle
+                str2 = text
+                str3 = endItemDesc
+            },
+            { "Chipbar updated. window=$str1 text=$str2 endItem=$str3" }
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/ChipbarLog.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
copy to packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/ChipbarLog.kt
index 581dafa3..5f101f2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/ChipbarLog.kt
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.domain.model
+package com.android.systemui.temporarydisplay.dagger
 
-/** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
-enum class KeyguardQuickAffordancePosition {
-    BOTTOM_START,
-    BOTTOM_END,
-}
+import javax.inject.Qualifier
+
+/** Status bar connectivity logs in table format. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class ChipbarLog
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
new file mode 100644
index 0000000..cf0a183
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.temporarydisplay.dagger
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.plugins.log.LogBuffer
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface TemporaryDisplayModule {
+    @Module
+    companion object {
+        @JvmStatic
+        @Provides
+        @SysUISingleton
+        @ChipbarLog
+        fun provideChipbarLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create("ChipbarLog", 40)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 3ecb15b..5894fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -115,7 +115,6 @@
     private final SecureSettings mSecureSettings;
     private final Executor mMainExecutor;
     private final Handler mBgHandler;
-    private final boolean mIsMonochromaticEnabled;
     private final Context mContext;
     private final boolean mIsMonetEnabled;
     private final UserTracker mUserTracker;
@@ -365,7 +364,6 @@
             UserTracker userTracker, DumpManager dumpManager, FeatureFlags featureFlags,
             @Main Resources resources, WakefulnessLifecycle wakefulnessLifecycle) {
         mContext = context;
-        mIsMonochromaticEnabled = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEMES);
         mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET);
         mDeviceProvisionedController = deviceProvisionedController;
         mBroadcastDispatcher = broadcastDispatcher;
@@ -669,11 +667,8 @@
         // used as a system-wide theme.
         // - Content intentionally excluded, intended for media player, not system-wide
         List<Style> validStyles = new ArrayList<>(Arrays.asList(Style.EXPRESSIVE, Style.SPRITZ,
-                Style.TONAL_SPOT, Style.FRUIT_SALAD, Style.RAINBOW, Style.VIBRANT));
-
-        if (mIsMonochromaticEnabled) {
-            validStyles.add(Style.MONOCHROMATIC);
-        }
+                Style.TONAL_SPOT, Style.FRUIT_SALAD, Style.RAINBOW, Style.VIBRANT,
+                Style.MONOCHROMATIC));
 
         Style style = mThemeStyle;
         final String overlayPackageJson = mSecureSettings.getStringForUser(
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index b16dc54..6a23260 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -62,6 +62,7 @@
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
 
 /**
@@ -136,7 +137,7 @@
     private val isNewImpl: Boolean
         get() = !featureFlags.isEnabled(Flags.USER_INTERACTOR_AND_REPO_USE_CONTROLLER)
 
-    private val _userSwitcherSettings = MutableStateFlow<UserSwitcherSettingsModel?>(null)
+    private val _userSwitcherSettings = MutableStateFlow(runBlocking { getSettings() })
     override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> =
         _userSwitcherSettings.asStateFlow().filterNotNull()
 
@@ -235,7 +236,7 @@
     }
 
     override fun isSimpleUserSwitcher(): Boolean {
-        return checkNotNull(_userSwitcherSettings.value?.isSimpleUserSwitcher)
+        return _userSwitcherSettings.value.isSimpleUserSwitcher
     }
 
     private fun observeSelectedUser() {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
index 07e5cf9..f9d14cd 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
@@ -208,7 +208,12 @@
             if (newGuestId == UserHandle.USER_NULL) {
                 Log.e(TAG, "Could not create new guest, switching back to system user")
                 switchUser(UserHandle.USER_SYSTEM)
-                withContext(backgroundDispatcher) { manager.removeUser(currentUser.id) }
+                withContext(backgroundDispatcher) {
+                    manager.removeUserWhenPossible(
+                        UserHandle.of(currentUser.id),
+                        /* overrideDevicePolicy= */ false
+                    )
+                }
                 try {
                     WindowManagerGlobal.getWindowManagerService().lockNow(/* options= */ null)
                 } catch (e: RemoteException) {
@@ -222,13 +227,21 @@
 
             switchUser(newGuestId)
 
-            withContext(backgroundDispatcher) { manager.removeUser(currentUser.id) }
+            withContext(backgroundDispatcher) {
+                manager.removeUserWhenPossible(
+                    UserHandle.of(currentUser.id),
+                    /* overrideDevicePolicy= */ false
+                )
+            }
         } else {
             if (repository.isGuestUserAutoCreated) {
                 repository.isGuestUserResetting = true
             }
             switchUser(targetUserId)
-            manager.removeUser(currentUser.id)
+            manager.removeUserWhenPossible(
+                UserHandle.of(currentUser.id),
+                /* overrideDevicePolicy= */ false
+            )
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
index 968af59..ad09ee3 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
@@ -61,14 +61,15 @@
         falsingCollector: FalsingCollector,
         onFinish: () -> Unit,
     ) {
-        val rootView: UserSwitcherRootView = view.requireViewById(R.id.user_switcher_root)
-        val flowWidget: FlowWidget = view.requireViewById(R.id.flow)
+        val gridContainerView: UserSwitcherRootView =
+            view.requireViewById(R.id.user_switcher_grid_container)
+        val flowWidget: FlowWidget = gridContainerView.requireViewById(R.id.flow)
         val addButton: View = view.requireViewById(R.id.add)
         val cancelButton: View = view.requireViewById(R.id.cancel)
         val popupMenuAdapter = MenuAdapter(layoutInflater)
         var popupMenu: UserSwitcherPopupMenu? = null
 
-        rootView.touchHandler =
+        gridContainerView.touchHandler =
             object : Gefingerpoken {
                 override fun onTouchEvent(ev: MotionEvent?): Boolean {
                     falsingCollector.onTouchEvent(ev)
@@ -134,7 +135,7 @@
                         val viewPool =
                             view.children.filter { it.tag == USER_VIEW_TAG }.toMutableList()
                         viewPool.forEach {
-                            view.removeView(it)
+                            gridContainerView.removeView(it)
                             flowWidget.removeView(it)
                         }
                         users.forEach { userViewModel ->
@@ -152,7 +153,7 @@
                                     inflatedView
                                 }
                             userView.id = View.generateViewId()
-                            view.addView(userView)
+                            gridContainerView.addView(userView)
                             flowWidget.addView(userView)
                             UserViewBinder.bind(
                                 view = userView,
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 44f6d03..ad97ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -31,6 +31,7 @@
 import android.os.HandlerThread;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.os.UserHandle;
 import android.service.wallpaper.WallpaperService;
 import android.util.ArraySet;
 import android.util.Log;
@@ -598,7 +599,6 @@
             getDisplayContext().getSystemService(DisplayManager.class)
                     .unregisterDisplayListener(this);
             mWallpaperLocalColorExtractor.cleanUp();
-            unloadBitmap();
         }
 
         @Override
@@ -676,9 +676,14 @@
         void drawFrameOnCanvas(Bitmap bitmap) {
             Trace.beginSection("ImageWallpaper.CanvasEngine#drawFrame");
             Surface surface = mSurfaceHolder.getSurface();
-            Canvas canvas = mWideColorGamut
-                    ? surface.lockHardwareWideColorGamutCanvas()
-                    : surface.lockHardwareCanvas();
+            Canvas canvas = null;
+            try {
+                canvas = mWideColorGamut
+                        ? surface.lockHardwareWideColorGamutCanvas()
+                        : surface.lockHardwareCanvas();
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "Unable to lock canvas", e);
+            }
             if (canvas != null) {
                 Rect dest = mSurfaceHolder.getSurfaceFrame();
                 try {
@@ -709,17 +714,6 @@
             }
         }
 
-        private void unloadBitmap() {
-            mBackgroundExecutor.execute(this::unloadBitmapSynchronized);
-        }
-
-        private void unloadBitmapSynchronized() {
-            synchronized (mLock) {
-                mBitmapUsages = 0;
-                unloadBitmapInternal();
-            }
-        }
-
         private void unloadBitmapInternal() {
             Trace.beginSection("ImageWallpaper.CanvasEngine#unloadBitmap");
             if (mBitmap != null) {
@@ -738,7 +732,7 @@
             boolean loadSuccess = false;
             Bitmap bitmap;
             try {
-                bitmap = mWallpaperManager.getBitmap(false);
+                bitmap = mWallpaperManager.getBitmapAsUser(UserHandle.USER_CURRENT, false);
                 if (bitmap != null
                         && bitmap.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) {
                     throw new RuntimeException("Wallpaper is too large to draw!");
@@ -757,7 +751,7 @@
                 }
 
                 try {
-                    bitmap = mWallpaperManager.getBitmap(false);
+                    bitmap = mWallpaperManager.getBitmapAsUser(UserHandle.USER_CURRENT, false);
                 } catch (RuntimeException | OutOfMemoryError e) {
                     Log.w(TAG, "Unable to load default wallpaper!", e);
                     bitmap = null;
@@ -770,9 +764,6 @@
                 Log.e(TAG, "Attempt to load a recycled bitmap");
             } else if (mBitmap == bitmap) {
                 Log.e(TAG, "Loaded a bitmap that was already loaded");
-            } else if (bitmap.getWidth() < 1 || bitmap.getHeight() < 1) {
-                Log.e(TAG, "Attempt to load an invalid wallpaper of length "
-                        + bitmap.getWidth() + "x" + bitmap.getHeight());
             } else {
                 // at this point, loading is done correctly.
                 loadSuccess = true;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 1c3656d..52b6b38 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -65,7 +65,6 @@
 class ClockEventControllerTest : SysuiTestCase() {
 
     @JvmField @Rule val mockito = MockitoJUnit.rule()
-    @Mock private lateinit var keyguardInteractor: KeyguardInteractor
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
     @Mock private lateinit var batteryController: BatteryController
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index b885d54..f9bec65 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -486,7 +486,7 @@
 
         registeredSwipeListener.onSwipeUp();
 
-        verify(mKeyguardUpdateMonitor).requestFaceAuth(true,
+        verify(mKeyguardUpdateMonitor).requestFaceAuth(
                 FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER);
     }
 
@@ -499,16 +499,15 @@
         registeredSwipeListener.onSwipeUp();
 
         verify(mKeyguardUpdateMonitor, never())
-                .requestFaceAuth(true,
-                        FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER);
+                .requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER);
     }
 
     @Test
     public void onSwipeUp_whenFaceDetectionIsTriggered_hidesBouncerMessage() {
         KeyguardSecurityContainer.SwipeListener registeredSwipeListener =
                 getRegisteredSwipeListener();
-        when(mKeyguardUpdateMonitor.requestFaceAuth(true,
-                FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER)).thenReturn(true);
+        when(mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER))
+                .thenReturn(true);
         setupGetSecurityView();
 
         registeredSwipeListener.onSwipeUp();
@@ -520,8 +519,8 @@
     public void onSwipeUp_whenFaceDetectionIsNotTriggered_retainsBouncerMessage() {
         KeyguardSecurityContainer.SwipeListener registeredSwipeListener =
                 getRegisteredSwipeListener();
-        when(mKeyguardUpdateMonitor.requestFaceAuth(true,
-                FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER)).thenReturn(false);
+        when(mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER))
+                .thenReturn(false);
         setupGetSecurityView();
 
         registeredSwipeListener.onSwipeUp();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 66be6ec..680c3b8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -676,7 +676,7 @@
         bouncerFullyVisibleAndNotGoingToSleep();
         mTestableLooper.processAllMessages();
 
-        boolean didFaceAuthRun = mKeyguardUpdateMonitor.requestFaceAuth(true,
+        boolean didFaceAuthRun = mKeyguardUpdateMonitor.requestFaceAuth(
                 NOTIFICATION_PANEL_CLICKED);
 
         assertThat(didFaceAuthRun).isTrue();
@@ -688,7 +688,7 @@
         biometricsDisabledForCurrentUser();
         mTestableLooper.processAllMessages();
 
-        boolean didFaceAuthRun = mKeyguardUpdateMonitor.requestFaceAuth(true,
+        boolean didFaceAuthRun = mKeyguardUpdateMonitor.requestFaceAuth(
                 NOTIFICATION_PANEL_CLICKED);
 
         assertThat(didFaceAuthRun).isFalse();
@@ -707,8 +707,7 @@
         // Stop scanning when bouncer becomes visible
         setKeyguardBouncerVisibility(true);
         clearInvocations(mFaceManager);
-        mKeyguardUpdateMonitor.requestFaceAuth(true,
-                FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
+        mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
         verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
                 anyBoolean());
     }
@@ -1695,7 +1694,7 @@
     }
 
     private void triggerSuccessfulFaceAuth() {
-        mKeyguardUpdateMonitor.requestFaceAuth(true, FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
+        mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
         verify(mFaceManager).authenticate(any(),
                 any(),
                 mAuthenticationCallbackCaptor.capture(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
index 8ef65dc..a36105e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
 import android.testing.AndroidTestingRunner;
@@ -47,7 +48,7 @@
                 stubWindowManager);
         final MenuView stubMenuView = new MenuView(mContext, stubMenuViewModel,
                 stubMenuViewAppearance);
-        mDismissView = new DismissView(mContext);
+        mDismissView = spy(new DismissView(mContext));
         mDismissAnimationController = new DismissAnimationController(mDismissView, stubMenuView);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index bf6d574..78ee627 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -43,6 +43,7 @@
 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;
 
@@ -54,6 +55,9 @@
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
+    @Mock
+    private DismissAnimationController.DismissCallback mStubDismissCallback;
+
     private RecyclerView mStubListView;
     private MenuView mMenuView;
     private MenuItemAccessibilityDelegate mMenuItemAccessibilityDelegate;
@@ -87,7 +91,7 @@
 
         mMenuItemAccessibilityDelegate.onInitializeAccessibilityNodeInfo(mStubListView, info);
 
-        assertThat(info.getActionList().size()).isEqualTo(5);
+        assertThat(info.getActionList().size()).isEqualTo(6);
     }
 
     @Test
@@ -156,6 +160,17 @@
     }
 
     @Test
+    public void performRemoveMenuAction_success() {
+        mMenuAnimationController.setDismissCallback(mStubDismissCallback);
+        final boolean removeMenuAction =
+                mMenuItemAccessibilityDelegate.performAccessibilityAction(mStubListView,
+                        R.id.action_remove_menu, null);
+
+        assertThat(removeMenuAction).isTrue();
+        verify(mMenuAnimationController).removeMenu();
+    }
+
+    @Test
     public void performFocusAction_fadeIn() {
         mMenuItemAccessibilityDelegate.performAccessibilityAction(mStubListView,
                 ACTION_ACCESSIBILITY_FOCUS, null);
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 d1107c6..45b8ce1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -41,10 +41,15 @@
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakePromptRepository
+import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor
+import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
 import org.junit.After
 import org.junit.Rule
 import org.junit.Test
@@ -80,6 +85,15 @@
     @Mock
     lateinit var interactionJankMonitor: InteractionJankMonitor
 
+    private val biometricPromptRepository = FakePromptRepository()
+    private val credentialInteractor = FakeCredentialInteractor()
+    private val bpCredentialInteractor = BiometricPromptCredentialInteractor(
+        Dispatchers.Main.immediate,
+        biometricPromptRepository,
+        credentialInteractor
+    )
+    private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
+
     private var authContainer: TestAuthContainerView? = null
 
     @After
@@ -466,6 +480,8 @@
         userManager,
         lockPatternUtils,
         interactionJankMonitor,
+        { bpCredentialInteractor },
+        { credentialViewModel },
         Handler(TestableLooper.get(this).looper),
         FakeExecutor(FakeSystemClock())
     ) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 88a806d..4dd46ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -89,6 +89,8 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
+import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
@@ -165,6 +167,11 @@
     private UdfpsLogger mUdfpsLogger;
     @Mock
     private InteractionJankMonitor mInteractionJankMonitor;
+    @Mock
+    private BiometricPromptCredentialInteractor mBiometricPromptCredentialInteractor;
+    @Mock
+    private CredentialViewModel mCredentialViewModel;
+
     @Captor
     private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor;
     @Captor
@@ -1009,6 +1016,7 @@
                     fingerprintManager, faceManager, udfpsControllerFactory,
                     sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle,
                     mUserManager, mLockPatternUtils, mUdfpsLogger, statusBarStateController,
+                    () -> mBiometricPromptCredentialInteractor, () -> mCredentialViewModel,
                     mInteractionJankMonitor, mHandler, mBackgroundExecutor, vibratorHelper);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 8820c16..1379a0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -22,12 +22,11 @@
 import android.hardware.biometrics.ComponentInfoInternal
 import android.hardware.biometrics.PromptInfo
 import android.hardware.biometrics.SensorProperties
-import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.face.FaceSensorProperties
+import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.fingerprint.FingerprintSensorProperties
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
 import android.os.Bundle
-
 import android.testing.ViewUtils
 import android.view.LayoutInflater
 
@@ -83,26 +82,31 @@
 internal fun fingerprintSensorPropertiesInternal(
     ids: List<Int> = listOf(0)
 ): List<FingerprintSensorPropertiesInternal> {
-    val componentInfo = listOf(
+    val componentInfo =
+        listOf(
             ComponentInfoInternal(
-                    "fingerprintSensor" /* componentId */,
-                    "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
-                    "00000001" /* serialNumber */, "" /* softwareVersion */
+                "fingerprintSensor" /* componentId */,
+                "vendor/model/revision" /* hardwareVersion */,
+                "1.01" /* firmwareVersion */,
+                "00000001" /* serialNumber */,
+                "" /* softwareVersion */
             ),
             ComponentInfoInternal(
-                    "matchingAlgorithm" /* componentId */,
-                    "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
-                    "vendor/version/revision" /* softwareVersion */
+                "matchingAlgorithm" /* componentId */,
+                "" /* hardwareVersion */,
+                "" /* firmwareVersion */,
+                "" /* serialNumber */,
+                "vendor/version/revision" /* softwareVersion */
             )
-    )
+        )
     return ids.map { id ->
         FingerprintSensorPropertiesInternal(
-                id,
-                SensorProperties.STRENGTH_STRONG,
-                5 /* maxEnrollmentsPerUser */,
-                componentInfo,
-                FingerprintSensorProperties.TYPE_REAR,
-                false /* resetLockoutRequiresHardwareAuthToken */
+            id,
+            SensorProperties.STRENGTH_STRONG,
+            5 /* maxEnrollmentsPerUser */,
+            componentInfo,
+            FingerprintSensorProperties.TYPE_REAR,
+            false /* resetLockoutRequiresHardwareAuthToken */
         )
     }
 }
@@ -111,28 +115,53 @@
 internal fun faceSensorPropertiesInternal(
     ids: List<Int> = listOf(1)
 ): List<FaceSensorPropertiesInternal> {
-    val componentInfo = listOf(
+    val componentInfo =
+        listOf(
             ComponentInfoInternal(
-                    "faceSensor" /* componentId */,
-                    "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
-                    "00000001" /* serialNumber */, "" /* softwareVersion */
+                "faceSensor" /* componentId */,
+                "vendor/model/revision" /* hardwareVersion */,
+                "1.01" /* firmwareVersion */,
+                "00000001" /* serialNumber */,
+                "" /* softwareVersion */
             ),
             ComponentInfoInternal(
-                    "matchingAlgorithm" /* componentId */,
-                    "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
-                    "vendor/version/revision" /* softwareVersion */
+                "matchingAlgorithm" /* componentId */,
+                "" /* hardwareVersion */,
+                "" /* firmwareVersion */,
+                "" /* serialNumber */,
+                "vendor/version/revision" /* softwareVersion */
             )
-    )
+        )
     return ids.map { id ->
         FaceSensorPropertiesInternal(
-                id,
-                SensorProperties.STRENGTH_STRONG,
-                2 /* maxEnrollmentsPerUser */,
-                componentInfo,
-                FaceSensorProperties.TYPE_RGB,
-                true /* supportsFaceDetection */,
-                true /* supportsSelfIllumination */,
-                false /* resetLockoutRequiresHardwareAuthToken */
+            id,
+            SensorProperties.STRENGTH_STRONG,
+            2 /* maxEnrollmentsPerUser */,
+            componentInfo,
+            FaceSensorProperties.TYPE_RGB,
+            true /* supportsFaceDetection */,
+            true /* supportsSelfIllumination */,
+            false /* resetLockoutRequiresHardwareAuthToken */
         )
     }
 }
+
+internal fun promptInfo(
+    title: String = "title",
+    subtitle: String = "sub",
+    description: String = "desc",
+    credentialTitle: String? = "cred title",
+    credentialSubtitle: String? = "cred sub",
+    credentialDescription: String? = "cred desc",
+    negativeButton: String = "neg",
+): PromptInfo {
+    val info = PromptInfo()
+    info.title = title
+    info.subtitle = subtitle
+    info.description = description
+    credentialTitle?.let { info.deviceCredentialTitle = it }
+    credentialSubtitle?.let { info.deviceCredentialSubtitle = it }
+    credentialDescription?.let { info.deviceCredentialDescription = it }
+    info.negativeButtonText = negativeButton
+    return info
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 49c6fd1..ed957db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -69,6 +69,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -169,6 +170,8 @@
     private FakeExecutor mFgExecutor;
     @Mock
     private UdfpsDisplayMode mUdfpsDisplayMode;
+    @Mock
+    private FeatureFlags mFeatureFlags;
 
     // Stuff for configuring mocks
     @Mock
@@ -250,6 +253,7 @@
                 mStatusBarKeyguardViewManager,
                 mDumpManager,
                 mKeyguardUpdateMonitor,
+                mFeatureFlags,
                 mFalsingManager,
                 mPowerManager,
                 mAccessibilityManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
new file mode 100644
index 0000000..2d5614c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
@@ -0,0 +1,81 @@
+package com.android.systemui.biometrics.data.repository
+
+import android.hardware.biometrics.PromptInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.data.model.PromptKind
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runBlockingTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(JUnit4::class)
+class PromptRepositoryImplTest : SysuiTestCase() {
+
+    @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var authController: AuthController
+
+    private lateinit var repository: PromptRepositoryImpl
+
+    @Before
+    fun setup() {
+        repository = PromptRepositoryImpl(authController)
+    }
+
+    @Test
+    fun isShowing() = runBlockingTest {
+        whenever(authController.isShowing).thenReturn(true)
+
+        val values = mutableListOf<Boolean>()
+        val job = launch { repository.isShowing.toList(values) }
+        assertThat(values).containsExactly(true)
+
+        withArgCaptor<AuthController.Callback> {
+            verify(authController).addCallback(capture())
+
+            value.onBiometricPromptShown()
+            assertThat(values).containsExactly(true, true)
+
+            value.onBiometricPromptDismissed()
+            assertThat(values).containsExactly(true, true, false).inOrder()
+
+            job.cancel()
+            verify(authController).removeCallback(eq(value))
+        }
+    }
+
+    @Test
+    fun setsAndUnsetsPrompt() = runBlockingTest {
+        val kind = PromptKind.PIN
+        val uid = 8
+        val challenge = 90L
+        val promptInfo = PromptInfo()
+
+        repository.setPrompt(promptInfo, uid, challenge, kind)
+
+        assertThat(repository.kind.value).isEqualTo(kind)
+        assertThat(repository.userId.value).isEqualTo(uid)
+        assertThat(repository.challenge.value).isEqualTo(challenge)
+        assertThat(repository.promptInfo.value).isSameInstanceAs(promptInfo)
+
+        repository.unsetPrompt()
+
+        assertThat(repository.promptInfo.value).isNull()
+        assertThat(repository.userId.value).isNull()
+        assertThat(repository.challenge.value).isNull()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
new file mode 100644
index 0000000..97d3e68
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
@@ -0,0 +1,216 @@
+package com.android.systemui.biometrics.domain.interactor
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResourcesManager
+import android.content.pm.UserInfo
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockscreenCredential
+import com.android.internal.widget.VerifyCredentialResponse
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.domain.model.BiometricOperationInfo
+import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
+import com.android.systemui.biometrics.domain.model.BiometricUserInfo
+import com.android.systemui.biometrics.promptInfo
+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
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+private const val USER_ID = 22
+private const val OPERATION_ID = 100L
+private const val MAX_ATTEMPTS = 5
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class CredentialInteractorImplTest : SysuiTestCase() {
+
+    @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var lockPatternUtils: LockPatternUtils
+    @Mock private lateinit var userManager: UserManager
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock private lateinit var devicePolicyResourcesManager: DevicePolicyResourcesManager
+
+    private val systemClock = FakeSystemClock()
+
+    private lateinit var interactor: CredentialInteractorImpl
+
+    @Before
+    fun setup() {
+        whenever(devicePolicyManager.resources).thenReturn(devicePolicyResourcesManager)
+        whenever(lockPatternUtils.getMaximumFailedPasswordsForWipe(anyInt()))
+            .thenReturn(MAX_ATTEMPTS)
+        whenever(userManager.getUserInfo(eq(USER_ID))).thenReturn(UserInfo(USER_ID, "", 0))
+        whenever(devicePolicyManager.getProfileWithMinimumFailedPasswordsForWipe(eq(USER_ID)))
+            .thenReturn(USER_ID)
+
+        interactor =
+            CredentialInteractorImpl(
+                mContext,
+                lockPatternUtils,
+                userManager,
+                devicePolicyManager,
+                systemClock
+            )
+    }
+
+    @Test
+    fun testStealthMode() {
+        for (value in listOf(true, false, false, true)) {
+            whenever(lockPatternUtils.isVisiblePatternEnabled(eq(USER_ID))).thenReturn(value)
+
+            assertThat(interactor.isStealthModeActive(USER_ID)).isEqualTo(!value)
+        }
+    }
+
+    @Test
+    fun testCredentialOwner() {
+        for (value in listOf(12, 8, 4)) {
+            whenever(userManager.getCredentialOwnerProfile(eq(USER_ID))).thenReturn(value)
+
+            assertThat(interactor.getCredentialOwnerOrSelfId(USER_ID)).isEqualTo(value)
+        }
+    }
+
+    @Test fun pinCredentialWhenGood() = pinCredential(goodCredential())
+
+    @Test fun pinCredentialWhenBad() = pinCredential(badCredential())
+
+    @Test fun pinCredentialWhenBadAndThrottled() = pinCredential(badCredential(timeout = 5_000))
+
+    private fun pinCredential(result: VerifyCredentialResponse) = runTest {
+        val usedAttempts = 1
+        whenever(lockPatternUtils.getCurrentFailedPasswordAttempts(eq(USER_ID)))
+            .thenReturn(usedAttempts)
+        whenever(lockPatternUtils.verifyCredential(any(), eq(USER_ID), anyInt())).thenReturn(result)
+        whenever(lockPatternUtils.verifyGatekeeperPasswordHandle(anyLong(), anyLong(), eq(USER_ID)))
+            .thenReturn(result)
+        whenever(lockPatternUtils.setLockoutAttemptDeadline(anyInt(), anyInt())).thenAnswer {
+            systemClock.elapsedRealtime() + (it.arguments[1] as Int)
+        }
+
+        // wrap in an async block so the test can advance the clock if throttling credential
+        // checks prevents the method from returning
+        val statusList = mutableListOf<CredentialStatus>()
+        interactor
+            .verifyCredential(pinRequest(), LockscreenCredential.createPin("1234"))
+            .toList(statusList)
+
+        val last = statusList.removeLastOrNull()
+        if (result.isMatched) {
+            assertThat(statusList).isEmpty()
+            val successfulResult = last as? CredentialStatus.Success.Verified
+            assertThat(successfulResult).isNotNull()
+            assertThat(successfulResult!!.hat).isEqualTo(result.gatekeeperHAT)
+
+            verify(lockPatternUtils).userPresent(eq(USER_ID))
+            verify(lockPatternUtils)
+                .removeGatekeeperPasswordHandle(eq(result.gatekeeperPasswordHandle))
+        } else {
+            val failedResult = last as? CredentialStatus.Fail.Error
+            assertThat(failedResult).isNotNull()
+            assertThat(failedResult!!.remainingAttempts)
+                .isEqualTo(if (result.timeout > 0) null else MAX_ATTEMPTS - usedAttempts - 1)
+            assertThat(failedResult.urgentMessage).isNull()
+
+            if (result.timeout > 0) { // failed and throttled
+                // messages are in the throttled errors, so the final Error.error is empty
+                assertThat(failedResult.error).isEmpty()
+                assertThat(statusList).isNotEmpty()
+                assertThat(statusList.filterIsInstance(CredentialStatus.Fail.Throttled::class.java))
+                    .hasSize(statusList.size)
+
+                verify(lockPatternUtils).setLockoutAttemptDeadline(eq(USER_ID), eq(result.timeout))
+            } else { // failed
+                assertThat(failedResult.error)
+                    .matches(Regex("(.*)try again(.*)", RegexOption.IGNORE_CASE).toPattern())
+                assertThat(statusList).isEmpty()
+
+                verify(lockPatternUtils).reportFailedPasswordAttempt(eq(USER_ID))
+            }
+        }
+    }
+
+    @Test
+    fun pinCredentialWhenBadAndFinalAttempt() = runTest {
+        whenever(lockPatternUtils.verifyCredential(any(), eq(USER_ID), anyInt()))
+            .thenReturn(badCredential())
+        whenever(lockPatternUtils.getCurrentFailedPasswordAttempts(eq(USER_ID)))
+            .thenReturn(MAX_ATTEMPTS - 2)
+
+        val statusList = mutableListOf<CredentialStatus>()
+        interactor
+            .verifyCredential(pinRequest(), LockscreenCredential.createPin("1234"))
+            .toList(statusList)
+
+        val result = statusList.removeLastOrNull() as? CredentialStatus.Fail.Error
+        assertThat(result).isNotNull()
+        assertThat(result!!.remainingAttempts).isEqualTo(1)
+        assertThat(result.urgentMessage).isNotEmpty()
+        assertThat(statusList).isEmpty()
+
+        verify(lockPatternUtils).reportFailedPasswordAttempt(eq(USER_ID))
+    }
+
+    @Test
+    fun pinCredentialWhenBadAndNoMoreAttempts() = runTest {
+        whenever(lockPatternUtils.verifyCredential(any(), eq(USER_ID), anyInt()))
+            .thenReturn(badCredential())
+        whenever(lockPatternUtils.getCurrentFailedPasswordAttempts(eq(USER_ID)))
+            .thenReturn(MAX_ATTEMPTS - 1)
+        whenever(devicePolicyResourcesManager.getString(any(), any())).thenReturn("wipe")
+
+        val statusList = mutableListOf<CredentialStatus>()
+        interactor
+            .verifyCredential(pinRequest(), LockscreenCredential.createPin("1234"))
+            .toList(statusList)
+
+        val result = statusList.removeLastOrNull() as? CredentialStatus.Fail.Error
+        assertThat(result).isNotNull()
+        assertThat(result!!.remainingAttempts).isEqualTo(0)
+        assertThat(result.urgentMessage).isNotEmpty()
+        assertThat(statusList).isEmpty()
+
+        verify(lockPatternUtils).reportFailedPasswordAttempt(eq(USER_ID))
+    }
+}
+
+private fun pinRequest(): BiometricPromptRequest.Credential.Pin =
+    BiometricPromptRequest.Credential.Pin(
+        promptInfo(),
+        BiometricUserInfo(USER_ID),
+        BiometricOperationInfo(OPERATION_ID)
+    )
+
+private fun goodCredential(
+    passwordHandle: Long = 90,
+    hat: ByteArray = ByteArray(69),
+): VerifyCredentialResponse =
+    VerifyCredentialResponse.Builder()
+        .setGatekeeperPasswordHandle(passwordHandle)
+        .setGatekeeperHAT(hat)
+        .build()
+
+private fun badCredential(timeout: Int = 0): VerifyCredentialResponse =
+    if (timeout > 0) {
+        VerifyCredentialResponse.fromTimeout(timeout)
+    } else {
+        VerifyCredentialResponse.fromError()
+    }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
new file mode 100644
index 0000000..dbcbf41
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
@@ -0,0 +1,270 @@
+package com.android.systemui.biometrics.domain.interactor
+
+import android.hardware.biometrics.PromptInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.data.repository.FakePromptRepository
+import com.android.systemui.biometrics.domain.model.BiometricOperationInfo
+import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
+import com.android.systemui.biometrics.domain.model.BiometricUserInfo
+import com.android.systemui.biometrics.promptInfo
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.junit.MockitoJUnit
+
+private const val USER_ID = 22
+private const val OPERATION_ID = 100L
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class PromptCredentialInteractorTest : SysuiTestCase() {
+
+    @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+    private val dispatcher = UnconfinedTestDispatcher()
+    private val biometricPromptRepository = FakePromptRepository()
+    private val credentialInteractor = FakeCredentialInteractor()
+
+    private lateinit var interactor: BiometricPromptCredentialInteractor
+
+    @Before
+    fun setup() {
+        interactor =
+            BiometricPromptCredentialInteractor(
+                dispatcher,
+                biometricPromptRepository,
+                credentialInteractor
+            )
+    }
+
+    @Test
+    fun testIsShowing() =
+        runTest(dispatcher) {
+            var showing = false
+            val job = launch { interactor.isShowing.collect { showing = it } }
+
+            biometricPromptRepository.setIsShowing(false)
+            assertThat(showing).isFalse()
+
+            biometricPromptRepository.setIsShowing(true)
+            assertThat(showing).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun testShowError() =
+        runTest(dispatcher) {
+            var error: CredentialStatus.Fail? = null
+            val job = launch { interactor.verificationError.collect { error = it } }
+
+            for (msg in listOf("once", "again")) {
+                interactor.setVerificationError(error(msg))
+                assertThat(error).isEqualTo(error(msg))
+            }
+
+            interactor.resetVerificationError()
+            assertThat(error).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun nullWhenNoPromptInfo() =
+        runTest(dispatcher) {
+            var prompt: BiometricPromptRequest? = null
+            val job = launch { interactor.prompt.collect { prompt = it } }
+
+            assertThat(prompt).isNull()
+
+            job.cancel()
+        }
+
+    @Test fun usePinCredentialForPrompt() = useCredentialForPrompt(Utils.CREDENTIAL_PIN)
+
+    @Test fun usePasswordCredentialForPrompt() = useCredentialForPrompt(Utils.CREDENTIAL_PASSWORD)
+
+    @Test fun usePatternCredentialForPrompt() = useCredentialForPrompt(Utils.CREDENTIAL_PATTERN)
+
+    private fun useCredentialForPrompt(kind: Int) =
+        runTest(dispatcher) {
+            val isStealth = false
+            credentialInteractor.stealthMode = isStealth
+
+            var prompt: BiometricPromptRequest? = null
+            val job = launch { interactor.prompt.collect { prompt = it } }
+
+            val title = "what a prompt"
+            val subtitle = "s"
+            val description = "something to see"
+
+            interactor.useCredentialsForAuthentication(
+                PromptInfo().also {
+                    it.title = title
+                    it.description = description
+                    it.subtitle = subtitle
+                },
+                kind = kind,
+                userId = USER_ID,
+                challenge = OPERATION_ID
+            )
+
+            val p = prompt as? BiometricPromptRequest.Credential
+            assertThat(p).isNotNull()
+            assertThat(p!!.title).isEqualTo(title)
+            assertThat(p.subtitle).isEqualTo(subtitle)
+            assertThat(p.description).isEqualTo(description)
+            assertThat(p.userInfo).isEqualTo(BiometricUserInfo(USER_ID))
+            assertThat(p.operationInfo).isEqualTo(BiometricOperationInfo(OPERATION_ID))
+            assertThat(p)
+                .isInstanceOf(
+                    when (kind) {
+                        Utils.CREDENTIAL_PIN -> BiometricPromptRequest.Credential.Pin::class.java
+                        Utils.CREDENTIAL_PASSWORD ->
+                            BiometricPromptRequest.Credential.Password::class.java
+                        Utils.CREDENTIAL_PATTERN ->
+                            BiometricPromptRequest.Credential.Pattern::class.java
+                        else -> throw Exception("wrong kind")
+                    }
+                )
+            if (p is BiometricPromptRequest.Credential.Pattern) {
+                assertThat(p.stealthMode).isEqualTo(isStealth)
+            }
+
+            interactor.resetPrompt()
+
+            assertThat(prompt).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun checkCredential() =
+        runTest(dispatcher) {
+            val hat = ByteArray(4)
+            credentialInteractor.verifyCredentialResponse = { _ -> flowOf(verified(hat)) }
+
+            val errors = mutableListOf<CredentialStatus.Fail?>()
+            val job = launch { interactor.verificationError.toList(errors) }
+
+            val checked =
+                interactor.checkCredential(pinRequest(), text = "1234")
+                    as? CredentialStatus.Success.Verified
+
+            assertThat(checked).isNotNull()
+            assertThat(checked!!.hat).isSameInstanceAs(hat)
+            assertThat(errors.map { it?.error }).containsExactly(null)
+
+            job.cancel()
+        }
+
+    @Test
+    fun checkCredentialWhenBad() =
+        runTest(dispatcher) {
+            val errorMessage = "bad"
+            val remainingAttempts = 12
+            credentialInteractor.verifyCredentialResponse = { _ ->
+                flowOf(error(errorMessage, remainingAttempts))
+            }
+
+            val errors = mutableListOf<CredentialStatus.Fail?>()
+            val job = launch { interactor.verificationError.toList(errors) }
+
+            val checked =
+                interactor.checkCredential(pinRequest(), text = "1234")
+                    as? CredentialStatus.Fail.Error
+
+            assertThat(checked).isNotNull()
+            assertThat(checked!!.remainingAttempts).isEqualTo(remainingAttempts)
+            assertThat(checked.urgentMessage).isNull()
+            assertThat(errors.map { it?.error }).containsExactly(null, errorMessage).inOrder()
+
+            job.cancel()
+        }
+
+    @Test
+    fun checkCredentialWhenBadAndUrgentMessage() =
+        runTest(dispatcher) {
+            val error = "not so bad"
+            val urgentMessage = "really bad"
+            credentialInteractor.verifyCredentialResponse = { _ ->
+                flowOf(error(error, 10, urgentMessage))
+            }
+
+            val errors = mutableListOf<CredentialStatus.Fail?>()
+            val job = launch { interactor.verificationError.toList(errors) }
+
+            val checked =
+                interactor.checkCredential(pinRequest(), text = "1234")
+                    as? CredentialStatus.Fail.Error
+
+            assertThat(checked).isNotNull()
+            assertThat(checked!!.urgentMessage).isEqualTo(urgentMessage)
+            assertThat(errors.map { it?.error }).containsExactly(null, error).inOrder()
+            assertThat(errors.last() as? CredentialStatus.Fail.Error)
+                .isEqualTo(error(error, 10, urgentMessage))
+
+            job.cancel()
+        }
+
+    @Test
+    fun checkCredentialWhenBadAndThrottled() =
+        runTest(dispatcher) {
+            val remainingAttempts = 3
+            val error = ":("
+            val urgentMessage = ":D"
+            credentialInteractor.verifyCredentialResponse = { _ ->
+                flow {
+                    for (i in 1..3) {
+                        emit(throttled("$i"))
+                        delay(100)
+                    }
+                    emit(error(error, remainingAttempts, urgentMessage))
+                }
+            }
+            val errors = mutableListOf<CredentialStatus.Fail?>()
+            val job = launch { interactor.verificationError.toList(errors) }
+
+            val checked =
+                interactor.checkCredential(pinRequest(), text = "1234")
+                    as? CredentialStatus.Fail.Error
+
+            assertThat(checked).isNotNull()
+            assertThat(checked!!.remainingAttempts).isEqualTo(remainingAttempts)
+            assertThat(checked.urgentMessage).isEqualTo(urgentMessage)
+            assertThat(errors.map { it?.error })
+                .containsExactly(null, "1", "2", "3", error)
+                .inOrder()
+
+            job.cancel()
+        }
+}
+
+private fun pinRequest(): BiometricPromptRequest.Credential.Pin =
+    BiometricPromptRequest.Credential.Pin(
+        promptInfo(),
+        BiometricUserInfo(USER_ID),
+        BiometricOperationInfo(OPERATION_ID)
+    )
+
+private fun verified(hat: ByteArray) = CredentialStatus.Success.Verified(hat)
+
+private fun throttled(error: String) = CredentialStatus.Fail.Throttled(error)
+
+private fun error(error: String? = null, remaining: Int? = null, urgentMessage: String? = null) =
+    CredentialStatus.Fail.Error(error, remaining, urgentMessage)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
new file mode 100644
index 0000000..4c5e3c1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
@@ -0,0 +1,93 @@
+package com.android.systemui.biometrics.domain.model
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.promptInfo
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+private const val USER_ID = 2
+private const val OPERATION_ID = 8L
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BiometricPromptRequestTest : SysuiTestCase() {
+
+    @Test
+    fun biometricRequestFromPromptInfo() {
+        val title = "what"
+        val subtitle = "a"
+        val description = "request"
+
+        val request =
+            BiometricPromptRequest.Biometric(
+                promptInfo(title = title, subtitle = subtitle, description = description),
+                BiometricUserInfo(USER_ID),
+                BiometricOperationInfo(OPERATION_ID)
+            )
+
+        assertThat(request.title).isEqualTo(title)
+        assertThat(request.subtitle).isEqualTo(subtitle)
+        assertThat(request.description).isEqualTo(description)
+        assertThat(request.userInfo).isEqualTo(BiometricUserInfo(USER_ID))
+        assertThat(request.operationInfo).isEqualTo(BiometricOperationInfo(OPERATION_ID))
+    }
+
+    @Test
+    fun credentialRequestFromPromptInfo() {
+        val title = "what"
+        val subtitle = "a"
+        val description = "request"
+        val stealth = true
+
+        val toCheck =
+            listOf(
+                BiometricPromptRequest.Credential.Pin(
+                    promptInfo(
+                        title = title,
+                        subtitle = subtitle,
+                        description = description,
+                        credentialTitle = null,
+                        credentialSubtitle = null,
+                        credentialDescription = null
+                    ),
+                    BiometricUserInfo(USER_ID),
+                    BiometricOperationInfo(OPERATION_ID)
+                ),
+                BiometricPromptRequest.Credential.Password(
+                    promptInfo(
+                        credentialTitle = title,
+                        credentialSubtitle = subtitle,
+                        credentialDescription = description
+                    ),
+                    BiometricUserInfo(USER_ID),
+                    BiometricOperationInfo(OPERATION_ID)
+                ),
+                BiometricPromptRequest.Credential.Pattern(
+                    promptInfo(
+                        subtitle = subtitle,
+                        description = description,
+                        credentialTitle = title,
+                        credentialSubtitle = null,
+                        credentialDescription = null
+                    ),
+                    BiometricUserInfo(USER_ID),
+                    BiometricOperationInfo(OPERATION_ID),
+                    stealth
+                )
+            )
+
+        for (request in toCheck) {
+            assertThat(request.title).isEqualTo(title)
+            assertThat(request.subtitle).isEqualTo(subtitle)
+            assertThat(request.description).isEqualTo(description)
+            assertThat(request.userInfo).isEqualTo(BiometricUserInfo(USER_ID))
+            assertThat(request.operationInfo).isEqualTo(BiometricOperationInfo(OPERATION_ID))
+            if (request is BiometricPromptRequest.Credential.Pattern) {
+                assertThat(request.stealthMode).isEqualTo(stealth)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
new file mode 100644
index 0000000..d73cdfc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
@@ -0,0 +1,181 @@
+package com.android.systemui.biometrics.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.model.PromptKind
+import com.android.systemui.biometrics.data.repository.FakePromptRepository
+import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor
+import com.android.systemui.biometrics.domain.interactor.CredentialStatus
+import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
+import com.android.systemui.biometrics.promptInfo
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+private const val USER_ID = 9
+private const val OPERATION_ID = 10L
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class CredentialViewModelTest : SysuiTestCase() {
+
+    private val dispatcher = UnconfinedTestDispatcher()
+    private val promptRepository = FakePromptRepository()
+    private val credentialInteractor = FakeCredentialInteractor()
+
+    private lateinit var viewModel: CredentialViewModel
+
+    @Before
+    fun setup() {
+        viewModel =
+            CredentialViewModel(
+                mContext,
+                BiometricPromptCredentialInteractor(
+                    dispatcher,
+                    promptRepository,
+                    credentialInteractor
+                )
+            )
+    }
+
+    @Test fun setsPinInputFlags() = setsInputFlags(PromptKind.PIN, expectFlags = true)
+    @Test fun setsPasswordInputFlags() = setsInputFlags(PromptKind.PASSWORD, expectFlags = false)
+    @Test fun setsPatternInputFlags() = setsInputFlags(PromptKind.PATTERN, expectFlags = false)
+
+    private fun setsInputFlags(type: PromptKind, expectFlags: Boolean) =
+        runTestWithKind(type) {
+            var flags: Int? = null
+            val job = launch { viewModel.inputFlags.collect { flags = it } }
+
+            if (expectFlags) {
+                assertThat(flags).isNotNull()
+            } else {
+                assertThat(flags).isNull()
+            }
+            job.cancel()
+        }
+
+    @Test fun isStealthIgnoredByPin() = isStealthMode(PromptKind.PIN, expectStealth = false)
+    @Test
+    fun isStealthIgnoredByPassword() = isStealthMode(PromptKind.PASSWORD, expectStealth = false)
+    @Test fun isStealthUsedByPattern() = isStealthMode(PromptKind.PATTERN, expectStealth = true)
+
+    private fun isStealthMode(type: PromptKind, expectStealth: Boolean) =
+        runTestWithKind(type, init = { credentialInteractor.stealthMode = true }) {
+            var stealth: Boolean? = null
+            val job = launch { viewModel.stealthMode.collect { stealth = it } }
+
+            assertThat(stealth).isEqualTo(expectStealth)
+
+            job.cancel()
+        }
+
+    @Test
+    fun animatesContents() = runTestWithKind {
+        val expected = arrayOf(true, false, true)
+        val animate = mutableListOf<Boolean>()
+        val job = launch { viewModel.animateContents.toList(animate) }
+
+        for (value in expected) {
+            viewModel.setAnimateContents(value)
+            viewModel.setAnimateContents(value)
+        }
+        assertThat(animate).containsExactly(*expected).inOrder()
+
+        job.cancel()
+    }
+
+    @Test
+    fun showAndClearErrors() = runTestWithKind {
+        var error = ""
+        val job = launch { viewModel.errorMessage.collect { error = it } }
+        assertThat(error).isEmpty()
+
+        viewModel.showPatternTooShortError()
+        assertThat(error).isNotEmpty()
+
+        viewModel.resetErrorMessage()
+        assertThat(error).isEmpty()
+
+        job.cancel()
+    }
+
+    @Test
+    fun checkCredential() = runTestWithKind {
+        val hat = ByteArray(2)
+        credentialInteractor.verifyCredentialResponse = { _ ->
+            flowOf(CredentialStatus.Success.Verified(hat))
+        }
+
+        val attestations = mutableListOf<ByteArray?>()
+        val remainingAttempts = mutableListOf<RemainingAttempts?>()
+        var header: HeaderViewModel? = null
+        val job = launch {
+            launch { viewModel.validatedAttestation.toList(attestations) }
+            launch { viewModel.remainingAttempts.toList(remainingAttempts) }
+            launch { viewModel.header.collect { header = it } }
+        }
+        assertThat(header).isNotNull()
+
+        viewModel.checkCredential("p", header!!)
+
+        val attestation = attestations.removeLastOrNull()
+        assertThat(attestation).isSameInstanceAs(hat)
+        assertThat(attestations).isEmpty()
+        assertThat(remainingAttempts).containsExactly(RemainingAttempts())
+
+        job.cancel()
+    }
+
+    @Test
+    fun checkCredentialWhenBad() = runTestWithKind {
+        val remaining = 2
+        val urgentError = "wow"
+        credentialInteractor.verifyCredentialResponse = { _ ->
+            flowOf(CredentialStatus.Fail.Error("error", remaining, urgentError))
+        }
+
+        val attestations = mutableListOf<ByteArray?>()
+        val remainingAttempts = mutableListOf<RemainingAttempts?>()
+        var header: HeaderViewModel? = null
+        val job = launch {
+            launch { viewModel.validatedAttestation.toList(attestations) }
+            launch { viewModel.remainingAttempts.toList(remainingAttempts) }
+            launch { viewModel.header.collect { header = it } }
+        }
+        assertThat(header).isNotNull()
+
+        viewModel.checkCredential("1111", header!!)
+
+        assertThat(attestations).containsExactly(null)
+
+        val attemptInfo = remainingAttempts.removeLastOrNull()
+        assertThat(attemptInfo).isNotNull()
+        assertThat(attemptInfo!!.remaining).isEqualTo(remaining)
+        assertThat(attemptInfo.message).isEqualTo(urgentError)
+        assertThat(remainingAttempts).containsExactly(RemainingAttempts()) // initial value
+
+        job.cancel()
+    }
+
+    private fun runTestWithKind(
+        kind: PromptKind = PromptKind.PIN,
+        init: () -> Unit = {},
+        block: suspend TestScope.() -> Unit,
+    ) =
+        runTest(dispatcher) {
+            init()
+            promptRepository.setPrompt(promptInfo(), USER_ID, OPERATION_ID, kind)
+            block()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index b7f1c1a..a872e4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -19,7 +19,9 @@
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHARE_TAPPED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
+import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -37,6 +39,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.screenshot.TimeoutHandler;
 
 import org.junit.After;
@@ -62,7 +65,10 @@
     @Mock
     private TimeoutHandler mTimeoutHandler;
     @Mock
+    private ClipboardOverlayUtils mClipboardUtils;
+    @Mock
     private UiEventLogger mUiEventLogger;
+    private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
 
     @Mock
     private Animator mAnimator;
@@ -73,7 +79,6 @@
     private ArgumentCaptor<ClipboardOverlayView.ClipboardOverlayCallbacks> mOverlayCallbacksCaptor;
     private ClipboardOverlayView.ClipboardOverlayCallbacks mCallbacks;
 
-
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -84,6 +89,8 @@
         mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
                 new ClipData.Item("Test Item"));
 
+        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, false);
+
         mOverlayController = new ClipboardOverlayController(
                 mContext,
                 mClipboardOverlayView,
@@ -91,6 +98,8 @@
                 getFakeBroadcastDispatcher(),
                 mBroadcastSender,
                 mTimeoutHandler,
+                mFeatureFlags,
+                mClipboardUtils,
                 mUiEventLogger);
         verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
         mCallbacks = mOverlayCallbacksCaptor.getValue();
@@ -186,4 +195,33 @@
         verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
         verify(mUiEventLogger, never()).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
     }
+
+    @Test
+    public void test_remoteCopy_withFlagOn() {
+        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
+        when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);
+
+        mOverlayController.setClipData(mSampleClipData, "");
+
+        verify(mTimeoutHandler, never()).resetTimeout();
+    }
+
+    @Test
+    public void test_remoteCopy_withFlagOff() {
+        when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);
+
+        mOverlayController.setClipData(mSampleClipData, "");
+
+        verify(mTimeoutHandler).resetTimeout();
+    }
+
+    @Test
+    public void test_nonRemoteCopy() {
+        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
+        when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(false);
+
+        mOverlayController.setClipData(mSampleClipData, "");
+
+        verify(mTimeoutHandler).resetTimeout();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
new file mode 100644
index 0000000..09b1699
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.clipboardoverlay;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.os.PersistableBundle;
+import android.testing.TestableResources;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ClipboardOverlayUtilsTest extends SysuiTestCase {
+
+    private ClipboardOverlayUtils mClipboardUtils;
+
+    @Before
+    public void setUp() {
+        mClipboardUtils = new ClipboardOverlayUtils();
+    }
+
+    @Test
+    public void test_extra_withPackage_returnsTrue() {
+        PersistableBundle b = new PersistableBundle();
+        b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, true);
+        ClipData data = constructClipData(
+                new String[]{"text/plain"}, new ClipData.Item("6175550000"), b);
+        TestableResources res = mContext.getOrCreateTestableResources();
+        res.addOverride(
+                R.string.config_remoteCopyPackage, "com.android.remote/.RemoteActivity");
+
+        assertTrue(mClipboardUtils.isRemoteCopy(mContext, data, "com.android.remote"));
+    }
+
+    @Test
+    public void test_noExtra_returnsFalse() {
+        ClipData data = constructClipData(
+                new String[]{"text/plain"}, new ClipData.Item("6175550000"), null);
+        TestableResources res = mContext.getOrCreateTestableResources();
+        res.addOverride(
+                R.string.config_remoteCopyPackage, "com.android.remote/.RemoteActivity");
+
+        assertFalse(mClipboardUtils.isRemoteCopy(mContext, data, "com.android.remote"));
+    }
+
+    @Test
+    public void test_falseExtra_returnsFalse() {
+        PersistableBundle b = new PersistableBundle();
+        b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, false);
+        ClipData data = constructClipData(
+                new String[]{"text/plain"}, new ClipData.Item("6175550000"), b);
+        TestableResources res = mContext.getOrCreateTestableResources();
+        res.addOverride(
+                R.string.config_remoteCopyPackage, "com.android.remote/.RemoteActivity");
+
+        assertFalse(mClipboardUtils.isRemoteCopy(mContext, data, "com.android.remote"));
+    }
+
+    @Test
+    public void test_wrongPackage_returnsFalse() {
+        PersistableBundle b = new PersistableBundle();
+        b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, true);
+        ClipData data = constructClipData(
+                new String[]{"text/plain"}, new ClipData.Item("6175550000"), b);
+
+        assertFalse(mClipboardUtils.isRemoteCopy(mContext, data, ""));
+    }
+
+    static ClipData constructClipData(String[] mimeTypes, ClipData.Item item,
+            PersistableBundle extras) {
+        ClipDescription description = new ClipDescription("Test", mimeTypes);
+        if (extras != null) {
+            description.setExtras(extras);
+        }
+        return new ClipData(description, item);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index 20a82c6..4b3b70e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -88,6 +88,7 @@
             flagMap,
             restarter
         )
+        mFeatureFlagsDebug.init()
         verify(flagManager).onSettingsChangedAction = any()
         broadcastReceiver = withArgCaptor {
             verify(mockContext).registerReceiver(capture(), any(), nullable(), nullable(),
@@ -255,11 +256,11 @@
         broadcastReceiver.onReceive(mockContext, Intent())
         broadcastReceiver.onReceive(mockContext, Intent("invalid action"))
         broadcastReceiver.onReceive(mockContext, Intent(FlagManager.ACTION_SET_FLAG))
-        setByBroadcast(0, false)     // unknown id does nothing
-        setByBroadcast(1, "string")  // wrong type does nothing
-        setByBroadcast(2, 123)       // wrong type does nothing
-        setByBroadcast(3, false)     // wrong type does nothing
-        setByBroadcast(4, 123)       // wrong type does nothing
+        setByBroadcast(0, false) // unknown id does nothing
+        setByBroadcast(1, "string") // wrong type does nothing
+        setByBroadcast(2, 123) // wrong type does nothing
+        setByBroadcast(3, false) // wrong type does nothing
+        setByBroadcast(4, 123) // wrong type does nothing
         verifyNoMoreInteractions(flagManager, secureSettings)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
index 575c142..b2dd60c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -38,8 +38,9 @@
 
     @Mock private lateinit var mResources: Resources
     @Mock private lateinit var mSystemProperties: SystemPropertiesHelper
+    @Mock private lateinit var restarter: Restarter
+    private val flagMap = mutableMapOf<Int, Flag<*>>()
     private val serverFlagReader = ServerFlagReaderFake()
-
     private val deviceConfig = DeviceConfigProxyFake()
 
     @Before
@@ -49,7 +50,9 @@
             mResources,
             mSystemProperties,
             deviceConfig,
-            serverFlagReader)
+            serverFlagReader,
+            flagMap,
+            restarter)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
new file mode 100644
index 0000000..6f5f460
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.flags
+
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ServerFlagReaderImplTest : SysuiTestCase() {
+
+    private val NAMESPACE = "test"
+
+    @Mock private lateinit var changeListener: ServerFlagReader.ChangeListener
+
+    private lateinit var serverFlagReader: ServerFlagReaderImpl
+    private val deviceConfig = DeviceConfigProxyFake()
+    private val executor = FakeExecutor(FakeSystemClock())
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        serverFlagReader = ServerFlagReaderImpl(NAMESPACE, deviceConfig, executor)
+    }
+
+    @Test
+    fun testChange_alertsListener() {
+        val flag = ReleasedFlag(1)
+        serverFlagReader.listenForChanges(listOf(flag), changeListener)
+
+        deviceConfig.setProperty(NAMESPACE, "flag_override_1", "1", false)
+        executor.runAllReady()
+
+        verify(changeListener).onChange()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
similarity index 62%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
index e99c139..f18acba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
@@ -15,10 +15,10 @@
  *
  */
 
-package com.android.systemui.keyguard.domain.quickaffordance
+package com.android.systemui.keyguard.data.quickaffordance
 
 import com.android.systemui.animation.Expandable
-import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.yield
@@ -29,24 +29,27 @@
  * This class is abstract to force tests to provide extensions of it as the system that references
  * these configs uses each implementation's class type to refer to them.
  */
-abstract class FakeKeyguardQuickAffordanceConfig : KeyguardQuickAffordanceConfig {
+abstract class FakeKeyguardQuickAffordanceConfig(
+    override val key: String,
+) : KeyguardQuickAffordanceConfig {
 
-    var onClickedResult: OnClickedResult = OnClickedResult.Handled
+    var onTriggeredResult: OnTriggeredResult = OnTriggeredResult.Handled
 
-    private val _state =
-        MutableStateFlow<KeyguardQuickAffordanceConfig.State>(
-            KeyguardQuickAffordanceConfig.State.Hidden
+    private val _lockScreenState =
+        MutableStateFlow<KeyguardQuickAffordanceConfig.LockScreenState>(
+            KeyguardQuickAffordanceConfig.LockScreenState.Hidden
         )
-    override val state: Flow<KeyguardQuickAffordanceConfig.State> = _state
+    override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
+        _lockScreenState
 
-    override fun onQuickAffordanceClicked(
+    override fun onTriggered(
         expandable: Expandable?,
-    ): OnClickedResult {
-        return onClickedResult
+    ): OnTriggeredResult {
+        return onTriggeredResult
     }
 
-    suspend fun setState(state: KeyguardQuickAffordanceConfig.State) {
-        _state.value = state
+    suspend fun setState(lockScreenState: KeyguardQuickAffordanceConfig.LockScreenState) {
+        _lockScreenState.value = lockScreenState
         // Yield to allow the test's collection coroutine to "catch up" and collect this value
         // before the test continues to the next line.
         // TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
similarity index 83%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index 9a91ea91..c94cec6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -1,21 +1,21 @@
 /*
- *  Copyright (C) 2022 The Android Open Source Project
+ * 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *       http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  *
  */
 
-package com.android.systemui.keyguard.domain.quickaffordance
+package com.android.systemui.keyguard.data.quickaffordance
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
@@ -122,8 +122,8 @@
                     emptyList()
                 }
             )
-        val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
-        val job = underTest.state.onEach(values::add).launchIn(this)
+        val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
+        val job = underTest.lockScreenState.onEach(values::add).launchIn(this)
 
         if (canShowWhileLocked) {
             verify(controlsListingController).addCallback(callbackCaptor.capture())
@@ -139,9 +139,9 @@
         assertThat(values.last())
             .isInstanceOf(
                 if (isVisibleExpected) {
-                    KeyguardQuickAffordanceConfig.State.Visible::class.java
+                    KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java
                 } else {
-                    KeyguardQuickAffordanceConfig.State.Hidden::class.java
+                    KeyguardQuickAffordanceConfig.LockScreenState.Hidden::class.java
                 }
             )
         job.cancel()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
similarity index 65%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index a809f05..659c1e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -1,21 +1,21 @@
 /*
- *  Copyright (C) 2022 The Android Open Source Project
+ * 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *       http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  *
  */
 
-package com.android.systemui.keyguard.domain.quickaffordance
+package com.android.systemui.keyguard.data.quickaffordance
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
@@ -23,7 +23,7 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
-import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
@@ -72,11 +72,11 @@
         whenever(component.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
         whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
 
-        val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
-        val job = underTest.state.onEach(values::add).launchIn(this)
+        val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
+        val job = underTest.lockScreenState.onEach(values::add).launchIn(this)
 
         assertThat(values.last())
-            .isInstanceOf(KeyguardQuickAffordanceConfig.State.Hidden::class.java)
+            .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Hidden::class.java)
         job.cancel()
     }
 
@@ -91,31 +91,32 @@
         whenever(component.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
         whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
 
-        val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
-        val job = underTest.state.onEach(values::add).launchIn(this)
+        val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
+        val job = underTest.lockScreenState.onEach(values::add).launchIn(this)
 
         assertThat(values.last())
-            .isInstanceOf(KeyguardQuickAffordanceConfig.State.Hidden::class.java)
+            .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Hidden::class.java)
         job.cancel()
     }
 
     @Test
-    fun `onQuickAffordanceClicked - canShowWhileLockedSetting is true`() = runBlockingTest {
+    fun `onQuickAffordanceTriggered - canShowWhileLockedSetting is true`() = runBlockingTest {
         whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true))
 
-        val onClickedResult = underTest.onQuickAffordanceClicked(expandable)
+        val onClickedResult = underTest.onTriggered(expandable)
 
-        assertThat(onClickedResult).isInstanceOf(OnClickedResult.StartActivity::class.java)
-        assertThat((onClickedResult as OnClickedResult.StartActivity).canShowWhileLocked).isTrue()
+        assertThat(onClickedResult).isInstanceOf(OnTriggeredResult.StartActivity::class.java)
+        assertThat((onClickedResult as OnTriggeredResult.StartActivity).canShowWhileLocked).isTrue()
     }
 
     @Test
-    fun `onQuickAffordanceClicked - canShowWhileLockedSetting is false`() = runBlockingTest {
+    fun `onQuickAffordanceTriggered - canShowWhileLockedSetting is false`() = runBlockingTest {
         whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
 
-        val onClickedResult = underTest.onQuickAffordanceClicked(expandable)
+        val onClickedResult = underTest.onTriggered(expandable)
 
-        assertThat(onClickedResult).isInstanceOf(OnClickedResult.StartActivity::class.java)
-        assertThat((onClickedResult as OnClickedResult.StartActivity).canShowWhileLocked).isFalse()
+        assertThat(onClickedResult).isInstanceOf(OnTriggeredResult.StartActivity::class.java)
+        assertThat((onClickedResult as OnTriggeredResult.StartActivity).canShowWhileLocked)
+            .isFalse()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
similarity index 70%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index 329c4db..61a3f9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
@@ -1,26 +1,26 @@
 /*
- *  Copyright (C) 2022 The Android Open Source Project
+ * 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *       http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  *
  */
 
-package com.android.systemui.keyguard.domain.quickaffordance
+package com.android.systemui.keyguard.data.quickaffordance
 
 import android.content.Intent
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
@@ -56,9 +56,9 @@
     @Test
     fun `affordance - sets up registration and delivers initial model`() = runBlockingTest {
         whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
-        var latest: KeyguardQuickAffordanceConfig.State? = null
+        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.state.onEach { latest = it }.launchIn(this)
+        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
 
         val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
         verify(controller).addCallback(callbackCaptor.capture())
@@ -77,8 +77,8 @@
     fun `affordance - scanner activity changed - delivers model with updated intent`() =
         runBlockingTest {
             whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
-            var latest: KeyguardQuickAffordanceConfig.State? = null
-            val job = underTest.state.onEach { latest = it }.launchIn(this)
+            var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+            val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
             val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
             verify(controller).addCallback(callbackCaptor.capture())
 
@@ -93,8 +93,8 @@
 
     @Test
     fun `affordance - scanner preference changed - delivers visible model`() = runBlockingTest {
-        var latest: KeyguardQuickAffordanceConfig.State? = null
-        val job = underTest.state.onEach { latest = it }.launchIn(this)
+        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
         val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
         verify(controller).addCallback(callbackCaptor.capture())
 
@@ -109,34 +109,35 @@
 
     @Test
     fun `affordance - scanner preference changed - delivers none`() = runBlockingTest {
-        var latest: KeyguardQuickAffordanceConfig.State? = null
-        val job = underTest.state.onEach { latest = it }.launchIn(this)
+        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
         val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
         verify(controller).addCallback(callbackCaptor.capture())
 
         whenever(controller.isEnabledForLockScreenButton).thenReturn(false)
         callbackCaptor.value.onQRCodeScannerPreferenceChanged()
 
-        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden)
+        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
 
         job.cancel()
         verify(controller).removeCallback(callbackCaptor.value)
     }
 
     @Test
-    fun onQuickAffordanceClicked() {
-        assertThat(underTest.onQuickAffordanceClicked(mock()))
+    fun onQuickAffordanceTriggered() {
+        assertThat(underTest.onTriggered(mock()))
             .isEqualTo(
-                OnClickedResult.StartActivity(
+                OnTriggeredResult.StartActivity(
                     intent = INTENT_1,
                     canShowWhileLocked = true,
                 )
             )
     }
 
-    private fun assertVisibleState(latest: KeyguardQuickAffordanceConfig.State?) {
-        assertThat(latest).isInstanceOf(KeyguardQuickAffordanceConfig.State.Visible::class.java)
-        val visibleState = latest as KeyguardQuickAffordanceConfig.State.Visible
+    private fun assertVisibleState(latest: KeyguardQuickAffordanceConfig.LockScreenState?) {
+        assertThat(latest)
+            .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
+        val visibleState = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
         assertThat(visibleState.icon).isNotNull()
         assertThat(visibleState.icon.contentDescription).isNotNull()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
similarity index 74%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 98dc4c4..c05beef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -1,21 +1,21 @@
 /*
- *  Copyright (C) 2022 The Android Open Source Project
+ * 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *       http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  *
  */
 
-package com.android.systemui.keyguard.domain.quickaffordance
+package com.android.systemui.keyguard.data.quickaffordance
 
 import android.graphics.drawable.Drawable
 import android.service.quickaccesswallet.GetWalletCardsResponse
@@ -67,11 +67,11 @@
     @Test
     fun `affordance - keyguard showing - has wallet card - visible model`() = runBlockingTest {
         setUpState()
-        var latest: KeyguardQuickAffordanceConfig.State? = null
+        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.state.onEach { latest = it }.launchIn(this)
+        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
 
-        val visibleModel = latest as KeyguardQuickAffordanceConfig.State.Visible
+        val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
         assertThat(visibleModel.icon)
             .isEqualTo(
                 Icon.Loaded(
@@ -88,11 +88,11 @@
     @Test
     fun `affordance - wallet not enabled - model is none`() = runBlockingTest {
         setUpState(isWalletEnabled = false)
-        var latest: KeyguardQuickAffordanceConfig.State? = null
+        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.state.onEach { latest = it }.launchIn(this)
+        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden)
+        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
 
         job.cancel()
     }
@@ -100,11 +100,11 @@
     @Test
     fun `affordance - query not successful - model is none`() = runBlockingTest {
         setUpState(isWalletQuerySuccessful = false)
-        var latest: KeyguardQuickAffordanceConfig.State? = null
+        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.state.onEach { latest = it }.launchIn(this)
+        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden)
+        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
 
         job.cancel()
     }
@@ -112,11 +112,11 @@
     @Test
     fun `affordance - missing icon - model is none`() = runBlockingTest {
         setUpState(hasWalletIcon = false)
-        var latest: KeyguardQuickAffordanceConfig.State? = null
+        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.state.onEach { latest = it }.launchIn(this)
+        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden)
+        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
 
         job.cancel()
     }
@@ -124,24 +124,24 @@
     @Test
     fun `affordance - no selected card - model is none`() = runBlockingTest {
         setUpState(hasWalletIcon = false)
-        var latest: KeyguardQuickAffordanceConfig.State? = null
+        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.state.onEach { latest = it }.launchIn(this)
+        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden)
+        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
 
         job.cancel()
     }
 
     @Test
-    fun onQuickAffordanceClicked() {
+    fun onQuickAffordanceTriggered() {
         val animationController: ActivityLaunchAnimator.Controller = mock()
         val expandable: Expandable = mock {
             whenever(this.activityLaunchController()).thenReturn(animationController)
         }
 
-        assertThat(underTest.onQuickAffordanceClicked(expandable))
-            .isEqualTo(KeyguardQuickAffordanceConfig.OnClickedResult.Handled)
+        assertThat(underTest.onTriggered(expandable))
+            .isEqualTo(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled)
         verify(walletController)
             .startQuickAccessUiIntent(
                 activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 7a15680..b9ab9d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -20,6 +20,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Position
 import com.android.systemui.doze.DozeHost
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.mockito.argumentCaptor
@@ -43,6 +45,7 @@
     @Mock private lateinit var statusBarStateController: StatusBarStateController
     @Mock private lateinit var dozeHost: DozeHost
     @Mock private lateinit var keyguardStateController: KeyguardStateController
+    @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
 
     private lateinit var underTest: KeyguardRepositoryImpl
 
@@ -55,6 +58,7 @@
                 statusBarStateController,
                 keyguardStateController,
                 dozeHost,
+                wakefulnessLifecycle,
             )
     }
 
@@ -184,4 +188,33 @@
         job.cancel()
         verify(statusBarStateController).removeCallback(captor.value)
     }
+
+    @Test
+    fun wakefullness() = runBlockingTest {
+        val values = mutableListOf<WakefulnessModel>()
+        val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
+
+        val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
+        verify(wakefulnessLifecycle).addObserver(captor.capture())
+
+        captor.value.onStartedWakingUp()
+        captor.value.onFinishedWakingUp()
+        captor.value.onStartedGoingToSleep()
+        captor.value.onFinishedGoingToSleep()
+
+        assertThat(values)
+            .isEqualTo(
+                listOf(
+                    // Initial value will be ASLEEP
+                    WakefulnessModel.ASLEEP,
+                    WakefulnessModel.STARTING_TO_WAKE,
+                    WakefulnessModel.AWAKE,
+                    WakefulnessModel.STARTING_TO_SLEEP,
+                    WakefulnessModel.ASLEEP,
+                )
+            )
+
+        job.cancel()
+        verify(wakefulnessLifecycle).removeObserver(captor.value)
+    }
 }
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 1b34100..64913c7c 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
@@ -63,7 +63,7 @@
 
     @Before
     fun setUp() {
-        underTest = KeyguardTransitionRepository()
+        underTest = KeyguardTransitionRepositoryImpl()
         wtfHandler = WtfHandler()
         oldWtfHandler = Log.setWtfHandler(wtfHandler)
     }
@@ -174,9 +174,6 @@
     }
 
     private fun assertSteps(steps: List<TransitionStep>, fractions: List<BigDecimal>) {
-        // + 2 accounts for start and finish of automated transition
-        assertThat(steps.size).isEqualTo(fractions.size + 2)
-
         assertThat(steps[0]).isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED))
         fractions.forEachIndexed { index, fraction ->
             assertThat(steps[index + 1])
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 b4d5464..7116cc1 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
@@ -25,11 +25,12 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
-import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
-import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -211,7 +212,11 @@
         MockitoAnnotations.initMocks(this)
         whenever(expandable.activityLaunchController()).thenReturn(animationController)
 
-        homeControls = object : FakeKeyguardQuickAffordanceConfig() {}
+        homeControls =
+            object :
+                FakeKeyguardQuickAffordanceConfig(
+                    BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
+                ) {}
         underTest =
             KeyguardQuickAffordanceInteractor(
                 keyguardInteractor = KeyguardInteractor(repository = FakeKeyguardRepository()),
@@ -224,8 +229,14 @@
                                 ),
                             KeyguardQuickAffordancePosition.BOTTOM_END to
                                 listOf(
-                                    object : FakeKeyguardQuickAffordanceConfig() {},
-                                    object : FakeKeyguardQuickAffordanceConfig() {},
+                                    object :
+                                        FakeKeyguardQuickAffordanceConfig(
+                                            BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
+                                        ) {},
+                                    object :
+                                        FakeKeyguardQuickAffordanceConfig(
+                                            BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER
+                                        ) {},
                                 ),
                         ),
                     ),
@@ -237,30 +248,30 @@
     }
 
     @Test
-    fun onQuickAffordanceClicked() = runBlockingTest {
+    fun onQuickAffordanceTriggered() = runBlockingTest {
         setUpMocks(
             needStrongAuthAfterBoot = needStrongAuthAfterBoot,
             keyguardIsUnlocked = keyguardIsUnlocked,
         )
 
         homeControls.setState(
-            state =
-                KeyguardQuickAffordanceConfig.State.Visible(
+            lockScreenState =
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                     icon = DRAWABLE,
                 )
         )
-        homeControls.onClickedResult =
+        homeControls.onTriggeredResult =
             if (startActivity) {
-                KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity(
+                KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
                     intent = INTENT,
                     canShowWhileLocked = canShowWhileLocked,
                 )
             } else {
-                KeyguardQuickAffordanceConfig.OnClickedResult.Handled
+                KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
             }
 
-        underTest.onQuickAffordanceClicked(
-            configKey = homeControls::class,
+        underTest.onQuickAffordanceTriggered(
+            configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
             expandable = expandable,
         )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 65fd6e5..ae32ba6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -22,13 +22,14 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
-import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
-import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordanceToggleState
+import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -69,9 +70,21 @@
         repository = FakeKeyguardRepository()
         repository.setKeyguardShowing(true)
 
-        homeControls = object : FakeKeyguardQuickAffordanceConfig() {}
-        quickAccessWallet = object : FakeKeyguardQuickAffordanceConfig() {}
-        qrCodeScanner = object : FakeKeyguardQuickAffordanceConfig() {}
+        homeControls =
+            object :
+                FakeKeyguardQuickAffordanceConfig(
+                    BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
+                ) {}
+        quickAccessWallet =
+            object :
+                FakeKeyguardQuickAffordanceConfig(
+                    BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
+                ) {}
+        qrCodeScanner =
+            object :
+                FakeKeyguardQuickAffordanceConfig(
+                    BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER
+                ) {}
 
         underTest =
             KeyguardQuickAffordanceInteractor(
@@ -99,11 +112,11 @@
 
     @Test
     fun `quickAffordance - bottom start affordance is visible`() = runBlockingTest {
-        val configKey = homeControls::class
+        val configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
         homeControls.setState(
-            KeyguardQuickAffordanceConfig.State.Visible(
+            KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                 icon = ICON,
-                toggle = KeyguardQuickAffordanceToggleState.On,
+                activationState = ActivationState.Active,
             )
         )
 
@@ -124,15 +137,15 @@
         assertThat(visibleModel.icon).isEqualTo(ICON)
         assertThat(visibleModel.icon.contentDescription)
             .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
-        assertThat(visibleModel.toggle).isEqualTo(KeyguardQuickAffordanceToggleState.On)
+        assertThat(visibleModel.activationState).isEqualTo(ActivationState.Active)
         job.cancel()
     }
 
     @Test
     fun `quickAffordance - bottom end affordance is visible`() = runBlockingTest {
-        val configKey = quickAccessWallet::class
+        val configKey = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
         quickAccessWallet.setState(
-            KeyguardQuickAffordanceConfig.State.Visible(
+            KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                 icon = ICON,
             )
         )
@@ -154,7 +167,7 @@
         assertThat(visibleModel.icon).isEqualTo(ICON)
         assertThat(visibleModel.icon.contentDescription)
             .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
-        assertThat(visibleModel.toggle).isEqualTo(KeyguardQuickAffordanceToggleState.NotSupported)
+        assertThat(visibleModel.activationState).isEqualTo(ActivationState.NotSupported)
         job.cancel()
     }
 
@@ -162,7 +175,7 @@
     fun `quickAffordance - bottom start affordance hidden while dozing`() = runBlockingTest {
         repository.setDozing(true)
         homeControls.setState(
-            KeyguardQuickAffordanceConfig.State.Visible(
+            KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                 icon = ICON,
             )
         )
@@ -182,7 +195,7 @@
         runBlockingTest {
             repository.setKeyguardShowing(false)
             homeControls.setState(
-                KeyguardQuickAffordanceConfig.State.Visible(
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                     icon = ICON,
                 )
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
new file mode 100644
index 0000000..0424c28
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -0,0 +1,149 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardTransitionInteractorTest : SysuiTestCase() {
+
+    private lateinit var underTest: KeyguardTransitionInteractor
+    private lateinit var repository: FakeKeyguardTransitionRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        underTest = KeyguardTransitionInteractor(repository)
+    }
+
+    @Test
+    fun `transition collectors receives only appropriate events`() =
+        runBlocking(IMMEDIATE) {
+            var goneToAodSteps = mutableListOf<TransitionStep>()
+            val job1 =
+                underTest.goneToAodTransition.onEach { goneToAodSteps.add(it) }.launchIn(this)
+
+            var aodToLockscreenSteps = mutableListOf<TransitionStep>()
+            val job2 =
+                underTest.aodToLockscreenTransition
+                    .onEach { aodToLockscreenSteps.add(it) }
+                    .launchIn(this)
+
+            val steps = mutableListOf<TransitionStep>()
+            steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
+            steps.add(TransitionStep(AOD, GONE, 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(GONE, AOD, 0f, STARTED))
+            steps.add(TransitionStep(GONE, AOD, 0.1f, RUNNING))
+            steps.add(TransitionStep(GONE, AOD, 0.2f, RUNNING))
+
+            steps.forEach { repository.sendTransitionStep(it) }
+
+            assertThat(aodToLockscreenSteps).isEqualTo(steps.subList(2, 5))
+            assertThat(goneToAodSteps).isEqualTo(steps.subList(5, 8))
+
+            job1.cancel()
+            job2.cancel()
+        }
+
+    @Test
+    fun dozeAmountTransitionTest() =
+        runBlocking(IMMEDIATE) {
+            var dozeAmountSteps = mutableListOf<TransitionStep>()
+            val job =
+                underTest.dozeAmountTransition.onEach { dozeAmountSteps.add(it) }.launchIn(this)
+
+            val steps = mutableListOf<TransitionStep>()
+
+            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(LOCKSCREEN, AOD, 0f, STARTED))
+            steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING))
+            steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+            steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+
+            steps.forEach { repository.sendTransitionStep(it) }
+
+            assertThat(dozeAmountSteps.subList(0, 3))
+                .isEqualTo(
+                    listOf(
+                        steps[0].copy(value = 1f - steps[0].value),
+                        steps[1].copy(value = 1f - steps[1].value),
+                        steps[2].copy(value = 1f - steps[2].value),
+                    )
+                )
+            assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
+
+            job.cancel()
+        }
+
+    @Test
+    fun keyguardStateTests() =
+        runBlocking(IMMEDIATE) {
+            var finishedSteps = mutableListOf<KeyguardState>()
+            val job1 =
+                underTest.finishedKeyguardState.onEach { finishedSteps.add(it) }.launchIn(this)
+            var startedSteps = mutableListOf<KeyguardState>()
+            val job2 = underTest.startedKeyguardState.onEach { startedSteps.add(it) }.launchIn(this)
+
+            val steps = mutableListOf<TransitionStep>()
+
+            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(LOCKSCREEN, AOD, 0f, STARTED))
+            steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+            steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+            steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+
+            steps.forEach { repository.sendTransitionStep(it) }
+
+            assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, AOD))
+            assertThat(startedSteps).isEqualTo(listOf(LOCKSCREEN, AOD, GONE))
+
+            job1.cancel()
+            job2.cancel()
+        }
+
+    companion object {
+        private val IMMEDIATE = Dispatchers.Main.immediate
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt
index e68c43f..13e2768 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt
@@ -17,8 +17,8 @@
 
 package com.android.systemui.keyguard.domain.quickaffordance
 
-import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
-import kotlin.reflect.KClass
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 
 /** Fake implementation of [FakeKeyguardQuickAffordanceRegistry], for tests. */
 class FakeKeyguardQuickAffordanceRegistry(
@@ -33,11 +33,8 @@
     }
 
     override fun get(
-        configClass: KClass<out FakeKeyguardQuickAffordanceConfig>
+        key: String,
     ): FakeKeyguardQuickAffordanceConfig {
-        return configsByPosition.values
-            .flatten()
-            .associateBy { config -> config::class }
-            .getValue(configClass)
+        return configsByPosition.values.flatten().associateBy { config -> config.key }.getValue(key)
     }
 }
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 d674c89..f73d1ec 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,15 +23,16 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
-import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
-import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
-import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordanceToggleState
+import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -40,7 +41,6 @@
 import com.google.common.truth.Truth.assertThat
 import kotlin.math.max
 import kotlin.math.min
-import kotlin.reflect.KClass
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.test.runBlockingTest
@@ -81,9 +81,21 @@
         whenever(burnInHelperWrapper.burnInOffset(anyInt(), any()))
             .thenReturn(RETURNED_BURN_IN_OFFSET)
 
-        homeControlsQuickAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
-        quickAccessWalletAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
-        qrCodeScannerAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
+        homeControlsQuickAffordanceConfig =
+            object :
+                FakeKeyguardQuickAffordanceConfig(
+                    BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
+                ) {}
+        quickAccessWalletAffordanceConfig =
+            object :
+                FakeKeyguardQuickAffordanceConfig(
+                    BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
+                ) {}
+        qrCodeScannerAffordanceConfig =
+            object :
+                FakeKeyguardQuickAffordanceConfig(
+                    BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER
+                ) {}
         registry =
             FakeKeyguardQuickAffordanceRegistry(
                 mapOf(
@@ -489,42 +501,42 @@
     private suspend fun setUpQuickAffordanceModel(
         position: KeyguardQuickAffordancePosition,
         testConfig: TestConfig,
-    ): KClass<out FakeKeyguardQuickAffordanceConfig> {
+    ): String {
         val config =
             when (position) {
                 KeyguardQuickAffordancePosition.BOTTOM_START -> homeControlsQuickAffordanceConfig
                 KeyguardQuickAffordancePosition.BOTTOM_END -> quickAccessWalletAffordanceConfig
             }
 
-        val state =
+        val lockScreenState =
             if (testConfig.isVisible) {
                 if (testConfig.intent != null) {
-                    config.onClickedResult =
-                        KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity(
+                    config.onTriggeredResult =
+                        KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
                             intent = testConfig.intent,
                             canShowWhileLocked = testConfig.canShowWhileLocked,
                         )
                 }
-                KeyguardQuickAffordanceConfig.State.Visible(
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                     icon = testConfig.icon ?: error("Icon is unexpectedly null!"),
-                    toggle =
+                    activationState =
                         when (testConfig.isActivated) {
-                            true -> KeyguardQuickAffordanceToggleState.On
-                            false -> KeyguardQuickAffordanceToggleState.Off
-                            null -> KeyguardQuickAffordanceToggleState.NotSupported
+                            true -> ActivationState.Active
+                            false -> ActivationState.Inactive
+                            null -> ActivationState.NotSupported
                         }
                 )
             } else {
-                KeyguardQuickAffordanceConfig.State.Hidden
+                KeyguardQuickAffordanceConfig.LockScreenState.Hidden
             }
-        config.setState(state)
-        return config::class
+        config.setState(lockScreenState)
+        return config.key
     }
 
     private fun assertQuickAffordanceViewModel(
         viewModel: KeyguardQuickAffordanceViewModel?,
         testConfig: TestConfig,
-        configKey: KClass<out FakeKeyguardQuickAffordanceConfig>,
+        configKey: String,
     ) {
         checkNotNull(viewModel)
         assertThat(viewModel.isVisible).isEqualTo(testConfig.isVisible)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
index 5bb74e5..a8f4138 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.media.controls.models.GutsViewHolder
 import com.android.systemui.media.controls.models.player.MediaViewHolder
 import com.android.systemui.monet.ColorScheme
+import com.android.systemui.ripple.MultiRippleController
 import junit.framework.Assert.assertEquals
 import org.junit.After
 import org.junit.Before
@@ -60,6 +61,7 @@
     private lateinit var animatingColorTransitionFactory: AnimatingColorTransitionFactory
     @Mock private lateinit var mediaViewHolder: MediaViewHolder
     @Mock private lateinit var gutsViewHolder: GutsViewHolder
+    @Mock private lateinit var multiRippleController: MultiRippleController
 
     @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
 
@@ -70,7 +72,12 @@
         whenever(extractColor.invoke(colorScheme)).thenReturn(TARGET_COLOR)
 
         colorSchemeTransition =
-            ColorSchemeTransition(context, mediaViewHolder, animatingColorTransitionFactory)
+            ColorSchemeTransition(
+                context,
+                mediaViewHolder,
+                multiRippleController,
+                animatingColorTransitionFactory
+            )
 
         colorTransition =
             object : AnimatingColorTransition(DEFAULT_COLOR, extractColor, applyColor) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index 5843053..8190156 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -59,6 +59,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bluetooth.BroadcastDialogController
 import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.media.controls.MediaTestUtils
 import com.android.systemui.media.controls.models.GutsViewHolder
 import com.android.systemui.media.controls.models.player.MediaAction
@@ -76,6 +78,7 @@
 import com.android.systemui.media.dialog.MediaOutputDialogFactory
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.ripple.MultiRippleView
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.animation.TransitionLayout
@@ -174,6 +177,7 @@
     private lateinit var cancelText: TextView
     private lateinit var dismiss: FrameLayout
     private lateinit var dismissText: TextView
+    private lateinit var multiRippleView: MultiRippleView
 
     private lateinit var session: MediaSession
     private lateinit var device: MediaDeviceData
@@ -205,6 +209,8 @@
     private lateinit var recSubtitle2: TextView
     private lateinit var recSubtitle3: TextView
     private var shouldShowBroadcastButton: Boolean = false
+    private val fakeFeatureFlag =
+        FakeFeatureFlags().apply { this.set(Flags.UMO_SURFACE_RIPPLE, false) }
 
     @JvmField @Rule val mockito = MockitoJUnit.rule()
 
@@ -244,7 +250,8 @@
                     keyguardStateController,
                     activityIntentHelper,
                     lockscreenUserManager,
-                    broadcastDialogController
+                    broadcastDialogController,
+                    fakeFeatureFlag
                 ) {
                 override fun loadAnimator(
                     animId: Int,
@@ -374,6 +381,8 @@
                     )
             }
 
+        multiRippleView = MultiRippleView(context, null)
+
         whenever(viewHolder.player).thenReturn(view)
         whenever(viewHolder.appIcon).thenReturn(appIcon)
         whenever(viewHolder.albumView).thenReturn(albumView)
@@ -414,6 +423,8 @@
         whenever(viewHolder.getAction(R.id.action4)).thenReturn(action4)
 
         whenever(viewHolder.actionsTopBarrier).thenReturn(actionsTopBarrier)
+
+        whenever(viewHolder.multiRippleView).thenReturn(multiRippleView)
     }
 
     /** Initialize elements for the recommendation view holder */
@@ -1973,6 +1984,50 @@
         assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE)
     }
 
+    @Test
+    fun onButtonClick_touchRippleFlagEnabled_playsTouchRipple() {
+        fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true)
+        val semanticActions =
+            MediaButton(
+                playOrPause =
+                    MediaAction(
+                        icon = null,
+                        action = {},
+                        contentDescription = "play",
+                        background = null
+                    )
+            )
+        val data = mediaData.copy(semanticActions = semanticActions)
+        player.attachPlayer(viewHolder)
+        player.bindPlayer(data, KEY)
+
+        viewHolder.actionPlayPause.callOnClick()
+
+        assertThat(viewHolder.multiRippleView.ripples.size).isEqualTo(1)
+    }
+
+    @Test
+    fun onButtonClick_touchRippleFlagDisabled_doesNotPlayTouchRipple() {
+        fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, false)
+        val semanticActions =
+            MediaButton(
+                playOrPause =
+                    MediaAction(
+                        icon = null,
+                        action = {},
+                        contentDescription = "play",
+                        background = null
+                    )
+            )
+        val data = mediaData.copy(semanticActions = semanticActions)
+        player.attachPlayer(viewHolder)
+        player.bindPlayer(data, KEY)
+
+        viewHolder.actionPlayPause.callOnClick()
+
+        assertThat(viewHolder.multiRippleView.ripples.size).isEqualTo(0)
+    }
+
     private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener =
         withArgCaptor {
             verify(seekBarViewModel).setScrubbingChangeListener(capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index 071604d..920801f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.media.dream.MediaDreamComplication
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.testing.FakeNotifPanelEvents
+import com.android.systemui.shade.ShadeExpansionStateManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -89,7 +89,7 @@
     private lateinit var mediaHierarchyManager: MediaHierarchyManager
     private lateinit var mediaFrame: ViewGroup
     private val configurationController = FakeConfigurationController()
-    private val notifPanelEvents = FakeNotifPanelEvents()
+    private val notifPanelEvents = ShadeExpansionStateManager()
     private val settings = FakeSettings()
     private lateinit var testableLooper: TestableLooper
     private lateinit var fakeHandler: FakeHandler
@@ -346,7 +346,7 @@
 
     @Test
     fun isCurrentlyInGuidedTransformation_hostsVisible_expandImmediateEnabled_returnsFalse() {
-        notifPanelEvents.changeExpandImmediate(expandImmediate = true)
+        notifPanelEvents.notifyExpandImmediateChange(true)
         goToLockscreen()
         enterGuidedTransformation()
         whenever(lockHost.visible).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index fdeb3f5..ad19bc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
+import com.android.systemui.temporarydisplay.chipbar.ChipbarLogger
 import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -80,6 +81,7 @@
     @Mock private lateinit var configurationController: ConfigurationController
     @Mock private lateinit var falsingManager: FalsingManager
     @Mock private lateinit var falsingCollector: FalsingCollector
+    @Mock private lateinit var chipbarLogger: ChipbarLogger
     @Mock private lateinit var logger: MediaTttLogger
     @Mock private lateinit var mediaTttFlags: MediaTttFlags
     @Mock private lateinit var packageManager: PackageManager
@@ -122,7 +124,7 @@
         chipbarCoordinator =
             FakeChipbarCoordinator(
                 context,
-                logger,
+                chipbarLogger,
                 windowManager,
                 fakeExecutor,
                 accessibilityManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/MultiRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ripple/MultiRippleControllerTest.kt
new file mode 100644
index 0000000..05512e5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ripple/MultiRippleControllerTest.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.ripple
+
+import android.graphics.Color
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.ripple.MultiRippleController.Companion.MAX_RIPPLE_NUMBER
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class MultiRippleControllerTest : SysuiTestCase() {
+    private lateinit var multiRippleController: MultiRippleController
+    private lateinit var multiRippleView: MultiRippleView
+    private lateinit var rippleAnimationConfig: RippleAnimationConfig
+    private val fakeSystemClock = FakeSystemClock()
+
+    // FakeExecutor is needed to run animator.
+    private val fakeExecutor = FakeExecutor(fakeSystemClock)
+
+    @Before
+    fun setup() {
+        rippleAnimationConfig = RippleAnimationConfig(duration = 1000L)
+        multiRippleView = MultiRippleView(context, null)
+        multiRippleController = MultiRippleController(multiRippleView)
+    }
+
+    @Test
+    fun updateColor_updatesColor() {
+        val initialColor = Color.WHITE
+        val expectedColor = Color.RED
+
+        fakeExecutor.execute {
+            val rippleAnimation =
+                RippleAnimation(rippleAnimationConfig.apply { this.color = initialColor })
+
+            with(multiRippleController) {
+                play(rippleAnimation)
+                updateColor(expectedColor)
+            }
+
+            assertThat(rippleAnimationConfig.color).isEqualTo(expectedColor)
+        }
+    }
+
+    @Test
+    fun play_playsRipple() {
+        fakeExecutor.execute {
+            val rippleAnimation = RippleAnimation(rippleAnimationConfig)
+
+            multiRippleController.play(rippleAnimation)
+
+            assertThat(multiRippleView.ripples.size).isEqualTo(1)
+            assertThat(multiRippleView.ripples[0]).isEqualTo(rippleAnimation)
+        }
+    }
+
+    @Test
+    fun play_doesNotExceedMaxRipple() {
+        fakeExecutor.execute {
+            for (i in 0..MAX_RIPPLE_NUMBER + 10) {
+                multiRippleController.play(RippleAnimation(rippleAnimationConfig))
+            }
+
+            assertThat(multiRippleView.ripples.size).isEqualTo(MAX_RIPPLE_NUMBER)
+        }
+    }
+
+    @Test
+    fun play_onEnd_removesAnimation() {
+        fakeExecutor.execute {
+            val rippleAnimation = RippleAnimation(rippleAnimationConfig)
+            multiRippleController.play(rippleAnimation)
+
+            assertThat(multiRippleView.ripples.size).isEqualTo(1)
+            assertThat(multiRippleView.ripples[0]).isEqualTo(rippleAnimation)
+
+            fakeSystemClock.advanceTime(rippleAnimationConfig.duration)
+
+            assertThat(multiRippleView.ripples.size).isEqualTo(0)
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleAnimationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleAnimationTest.kt
new file mode 100644
index 0000000..7662282
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleAnimationTest.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.ripple
+
+import android.graphics.Color
+import android.testing.AndroidTestingRunner
+import androidx.core.graphics.ColorUtils
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class RippleAnimationTest : SysuiTestCase() {
+
+    private val fakeSystemClock = FakeSystemClock()
+    private val fakeExecutor = FakeExecutor(fakeSystemClock)
+
+    @Test
+    fun init_shaderHasCorrectConfig() {
+        val config =
+            RippleAnimationConfig(
+                duration = 3000L,
+                pixelDensity = 2f,
+                color = Color.RED,
+                opacity = 30,
+                shouldFillRipple = true,
+                sparkleStrength = 0.3f
+            )
+        val rippleAnimation = RippleAnimation(config)
+
+        with(rippleAnimation.rippleShader) {
+            assertThat(rippleFill).isEqualTo(config.shouldFillRipple)
+            assertThat(pixelDensity).isEqualTo(config.pixelDensity)
+            assertThat(color).isEqualTo(ColorUtils.setAlphaComponent(config.color, config.opacity))
+            assertThat(sparkleStrength).isEqualTo(config.sparkleStrength)
+        }
+    }
+
+    @Test
+    fun updateColor_updatesColorCorrectly() {
+        val initialColor = Color.WHITE
+        val expectedColor = Color.RED
+        val config = RippleAnimationConfig(color = initialColor)
+        val rippleAnimation = RippleAnimation(config)
+
+        fakeExecutor.execute {
+            with(rippleAnimation) {
+                play()
+                updateColor(expectedColor)
+            }
+
+            assertThat(config.color).isEqualTo(expectedColor)
+        }
+    }
+
+    @Test
+    fun play_updatesIsPlaying() {
+        val config = RippleAnimationConfig(duration = 1000L)
+        val rippleAnimation = RippleAnimation(config)
+
+        fakeExecutor.execute {
+            rippleAnimation.play()
+
+            assertThat(rippleAnimation.isPlaying()).isTrue()
+
+            // move time to finish the animation
+            fakeSystemClock.advanceTime(config.duration)
+
+            assertThat(rippleAnimation.isPlaying()).isFalse()
+        }
+    }
+
+    @Test
+    fun play_onEnd_triggersOnAnimationEnd() {
+        val config = RippleAnimationConfig(duration = 1000L)
+        val rippleAnimation = RippleAnimation(config)
+        var animationEnd = false
+
+        fakeExecutor.execute {
+            rippleAnimation.play(onAnimationEnd = { animationEnd = true })
+
+            fakeSystemClock.advanceTime(config.duration)
+
+            assertThat(animationEnd).isTrue()
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
index 4c44dac..f4bc232 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
@@ -23,6 +23,7 @@
 
 import static java.nio.charset.StandardCharsets.US_ASCII;
 
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.graphics.Bitmap;
@@ -31,9 +32,11 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.net.Uri;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.UserHandle;
 import android.provider.MediaStore;
 import android.testing.AndroidTestingRunner;
 
@@ -41,11 +44,18 @@
 import androidx.test.filters.MediumTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+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.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -60,7 +70,6 @@
 @RunWith(AndroidTestingRunner.class)
 @MediumTest // file I/O
 public class ImageExporterTest extends SysuiTestCase {
-
     /** Executes directly in the caller's thread */
     private static final Executor DIRECT_EXECUTOR = Runnable::run;
     private static final byte[] EXIF_FILE_TAG = "Exif\u0000\u0000".getBytes(US_ASCII);
@@ -68,6 +77,15 @@
     private static final ZonedDateTime CAPTURE_TIME =
             ZonedDateTime.of(LocalDateTime.of(2020, 12, 15, 13, 15), ZoneId.of("EST"));
 
+    private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+    @Mock
+    private ContentResolver mMockContentResolver;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
     @Test
     public void testImageFilename() {
         assertEquals("image file name", "Screenshot_20201215-131500.png",
@@ -92,7 +110,8 @@
     @Test
     public void testImageExport() throws ExecutionException, InterruptedException, IOException {
         ContentResolver contentResolver = mContext.getContentResolver();
-        ImageExporter exporter = new ImageExporter(contentResolver);
+        mFeatureFlags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true);
+        ImageExporter exporter = new ImageExporter(contentResolver, mFeatureFlags);
 
         UUID requestId = UUID.fromString("3c11da99-9284-4863-b1d5-6f3684976814");
         Bitmap original = createCheckerBitmap(10, 10, 10);
@@ -168,6 +187,44 @@
                 values.getAsLong(MediaStore.MediaColumns.DATE_EXPIRES));
     }
 
+    @Test
+    public void testSetUser() {
+        mFeatureFlags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true);
+        ImageExporter exporter = new ImageExporter(mMockContentResolver, mFeatureFlags);
+
+        UserHandle imageUserHande = UserHandle.of(10);
+
+        ArgumentCaptor<Uri> uriCaptor = ArgumentCaptor.forClass(Uri.class);
+        // Capture the URI and then return null to bail out of export.
+        Mockito.when(mMockContentResolver.insert(uriCaptor.capture(), Mockito.any())).thenReturn(
+                null);
+        exporter.export(DIRECT_EXECUTOR, UUID.fromString("3c11da99-9284-4863-b1d5-6f3684976814"),
+                null, CAPTURE_TIME, imageUserHande);
+
+        Uri expected = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+        expected = ContentProvider.maybeAddUserId(expected, imageUserHande.getIdentifier());
+
+        assertEquals(expected, uriCaptor.getValue());
+    }
+
+    @Test
+    public void testSetUser_noWorkProfile() {
+        mFeatureFlags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false);
+        ImageExporter exporter = new ImageExporter(mMockContentResolver, mFeatureFlags);
+
+        UserHandle imageUserHandle = UserHandle.of(10);
+
+        ArgumentCaptor<Uri> uriCaptor = ArgumentCaptor.forClass(Uri.class);
+        // Capture the URI and then return null to bail out of export.
+        Mockito.when(mMockContentResolver.insert(uriCaptor.capture(), Mockito.any())).thenReturn(
+                null);
+        exporter.export(DIRECT_EXECUTOR, UUID.fromString("3c11da99-9284-4863-b1d5-6f3684976814"),
+                null, CAPTURE_TIME, imageUserHandle);
+
+        // The user handle should be ignored here since the flag is off.
+        assertEquals(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, uriCaptor.getValue());
+    }
+
     @SuppressWarnings("SameParameterValue")
     private Bitmap createCheckerBitmap(int tileSize, int w, int h) {
         Bitmap bitmap = Bitmap.createBitmap(w * tileSize, h * tileSize, Bitmap.Config.ARGB_8888);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 3a4da86..fa1fedb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -62,7 +62,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 private const val USER_ID = 1
-private const val TASK_ID = 1
+private const val TASK_ID = 11
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 02f28a2..93a1243 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -438,8 +438,6 @@
         when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
 
         mMainHandler = new Handler(Looper.getMainLooper());
-        NotificationPanelViewController.PanelEventsEmitter panelEventsEmitter =
-                new NotificationPanelViewController.PanelEventsEmitter();
 
         mNotificationPanelViewController = new NotificationPanelViewController(
                 mView,
@@ -495,7 +493,6 @@
                 () -> mKeyguardBottomAreaViewController,
                 mKeyguardUnlockAnimationController,
                 mNotificationListContainer,
-                panelEventsEmitter,
                 mNotificationStackSizeCalculator,
                 mUnlockedScreenOffAnimationController,
                 mShadeTransitionController,
@@ -1285,7 +1282,7 @@
         mNotificationPanelViewController.expandWithQs();
 
         verify(mLockscreenShadeTransitionController).goToLockedShade(
-                /* expandedView= */null, /* needsQSAnimation= */false);
+                /* expandedView= */null, /* needsQSAnimation= */true);
     }
 
     @Test
@@ -1555,7 +1552,7 @@
                 /* delay= */ 0
         );
 
-        mNotificationPanelViewController.setQsExpansion(/* height= */ 123);
+        mNotificationPanelViewController.setQsExpansionHeight(/* height= */ 123);
 
         // First for setTransitionToFullShadeAmount and then setQsExpansion
         verify(mQs, times(2)).setQsExpansion(
@@ -1576,7 +1573,7 @@
         when(mNotificationStackScrollLayoutController.getNotificationSquishinessFraction())
                 .thenReturn(nsslSquishinessFraction);
 
-        mNotificationPanelViewController.setQsExpansion(/* height= */ 123);
+        mNotificationPanelViewController.setQsExpansionHeight(/* height= */ 123);
 
         verify(mQs).setQsExpansion(
                 /* expansion= */ anyFloat(),
@@ -1597,7 +1594,7 @@
         mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
         mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
 
-        verify(mUpdateMonitor).requestFaceAuth(true,
+        verify(mUpdateMonitor).requestFaceAuth(
                 FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED);
     }
 
@@ -1607,7 +1604,7 @@
                 mNotificationPanelViewController.mStatusBarStateListener;
         statusBarStateListener.onStateChanged(KEYGUARD);
         mNotificationPanelViewController.setDozing(false, false);
-        when(mUpdateMonitor.requestFaceAuth(true, NOTIFICATION_PANEL_CLICKED)).thenReturn(false);
+        when(mUpdateMonitor.requestFaceAuth(NOTIFICATION_PANEL_CLICKED)).thenReturn(false);
 
         // This sets the dozing state that is read when onMiddleClicked is eventually invoked.
         mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
@@ -1622,7 +1619,7 @@
                 mNotificationPanelViewController.mStatusBarStateListener;
         statusBarStateListener.onStateChanged(KEYGUARD);
         mNotificationPanelViewController.setDozing(false, false);
-        when(mUpdateMonitor.requestFaceAuth(true, NOTIFICATION_PANEL_CLICKED)).thenReturn(true);
+        when(mUpdateMonitor.requestFaceAuth(NOTIFICATION_PANEL_CLICKED)).thenReturn(true);
 
         // This sets the dozing state that is read when onMiddleClicked is eventually invoked.
         mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
@@ -1642,7 +1639,7 @@
         mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
         mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
 
-        verify(mUpdateMonitor, never()).requestFaceAuth(anyBoolean(), anyString());
+        verify(mUpdateMonitor, never()).requestFaceAuth(anyString());
     }
 
     @Test
@@ -1653,7 +1650,7 @@
 
         mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
 
-        verify(mUpdateMonitor, never()).requestFaceAuth(anyBoolean(), anyString());
+        verify(mUpdateMonitor, never()).requestFaceAuth(anyString());
 
     }
 
@@ -1683,6 +1680,15 @@
         inOrder.verify(mNotificationStackScrollLayoutController).onExpansionStopped();
     }
 
+    @Test
+    public void inUnlockedSplitShade_transitioningMaxTransitionDistance_makesShadeFullyExpanded() {
+        mStatusBarStateController.setState(SHADE);
+        enableSplitShade(true);
+        int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
+        mNotificationPanelViewController.setExpandedHeight(transitionDistance);
+        assertThat(mNotificationPanelViewController.isFullyExpanded()).isTrue();
+    }
+
     private static MotionEvent createMotionEvent(int x, int y, int action) {
         return MotionEvent.obtain(
                 /* downTime= */ 0, /* eventTime= */ 0, action, x, y, /* metaState= */ 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/testing/FakeNotifPanelEvents.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/testing/FakeNotifPanelEvents.kt
deleted file mode 100644
index d052138..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/testing/FakeNotifPanelEvents.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shade.testing
-
-import com.android.systemui.shade.NotifPanelEvents
-
-/** Fake implementation of [NotifPanelEvents] for testing. */
-class FakeNotifPanelEvents : NotifPanelEvents {
-
-    private val listeners = mutableListOf<NotifPanelEvents.Listener>()
-
-    override fun registerListener(listener: NotifPanelEvents.Listener) {
-        listeners.add(listener)
-    }
-
-    override fun unregisterListener(listener: NotifPanelEvents.Listener) {
-        listeners.remove(listener)
-    }
-
-    fun changeExpandImmediate(expandImmediate: Boolean) {
-        listeners.forEach { it.onExpandImmediateChanged(expandImmediate) }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplingInstanceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
similarity index 64%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplingInstanceTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
index 09d51f6..5a62cc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplingInstanceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
@@ -21,61 +21,55 @@
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
-class RegionSamplingInstanceTest : SysuiTestCase() {
+class RegionSamplerTest : SysuiTestCase() {
 
-    @JvmField @Rule
-    val mockito = MockitoJUnit.rule()
+    @JvmField @Rule val mockito = MockitoJUnit.rule()
 
     @Mock private lateinit var sampledView: View
     @Mock private lateinit var mainExecutor: Executor
     @Mock private lateinit var bgExecutor: Executor
     @Mock private lateinit var regionSampler: RegionSamplingHelper
-    @Mock private lateinit var updateFun: RegionSamplingInstance.UpdateColorCallback
     @Mock private lateinit var pw: PrintWriter
     @Mock private lateinit var callback: RegionSamplingHelper.SamplingCallback
 
-    private lateinit var regionSamplingInstance: RegionSamplingInstance
+    private lateinit var mRegionSampler: RegionSampler
+    private var updateFun: UpdateColorCallback = {}
 
     @Before
     fun setUp() {
         whenever(sampledView.isAttachedToWindow).thenReturn(true)
-        whenever(regionSampler.callback).thenReturn(this@RegionSamplingInstanceTest.callback)
+        whenever(regionSampler.callback).thenReturn(this@RegionSamplerTest.callback)
 
-        regionSamplingInstance = object : RegionSamplingInstance(
-                sampledView,
-                mainExecutor,
-                bgExecutor,
-                true,
-                updateFun
-        ) {
-            override fun createRegionSamplingHelper(
+        mRegionSampler =
+            object : RegionSampler(sampledView, mainExecutor, bgExecutor, true, updateFun) {
+                override fun createRegionSamplingHelper(
                     sampledView: View,
                     callback: RegionSamplingHelper.SamplingCallback,
                     mainExecutor: Executor?,
                     bgExecutor: Executor?
-            ): RegionSamplingHelper {
-                return this@RegionSamplingInstanceTest.regionSampler
+                ): RegionSamplingHelper {
+                    return this@RegionSamplerTest.regionSampler
+                }
             }
-        }
     }
 
     @Test
     fun testStartRegionSampler() {
-        regionSamplingInstance.startRegionSampler()
+        mRegionSampler.startRegionSampler()
 
         verify(regionSampler).start(Rect(0, 0, 0, 0))
     }
 
     @Test
     fun testStopRegionSampler() {
-        regionSamplingInstance.stopRegionSampler()
+        mRegionSampler.stopRegionSampler()
 
         verify(regionSampler).stop()
     }
 
     @Test
     fun testDump() {
-        regionSamplingInstance.dump(pw)
+        mRegionSampler.dump(pw)
 
         verify(regionSampler).dump(pw)
     }
@@ -91,23 +85,18 @@
 
     @Test
     fun testFlagFalse() {
-        regionSamplingInstance = object : RegionSamplingInstance(
-                sampledView,
-                mainExecutor,
-                bgExecutor,
-                false,
-                updateFun
-        ) {
-            override fun createRegionSamplingHelper(
+        mRegionSampler =
+            object : RegionSampler(sampledView, mainExecutor, bgExecutor, false, updateFun) {
+                override fun createRegionSamplingHelper(
                     sampledView: View,
                     callback: RegionSamplingHelper.SamplingCallback,
                     mainExecutor: Executor?,
                     bgExecutor: Executor?
-            ): RegionSamplingHelper {
-                return this@RegionSamplingInstanceTest.regionSampler
+                ): RegionSamplingHelper {
+                    return this@RegionSamplerTest.regionSampler
+                }
             }
-        }
 
-        Assert.assertEquals(regionSamplingInstance.regionSampler, null)
+        Assert.assertEquals(mRegionSampler.regionSampler, null)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index c961cec..b4a5f5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -36,7 +36,8 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotifPanelEvents;
+import com.android.systemui.shade.ShadeStateEvents;
+import com.android.systemui.shade.ShadeStateEvents.ShadeStateEventsListener;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -71,12 +72,12 @@
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
     @Mock private HeadsUpManager mHeadsUpManager;
-    @Mock private NotifPanelEvents mNotifPanelEvents;
+    @Mock private ShadeStateEvents mShadeStateEvents;
     @Mock private VisualStabilityProvider mVisualStabilityProvider;
 
     @Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
     @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor;
-    @Captor private ArgumentCaptor<NotifPanelEvents.Listener> mNotifPanelEventsCallbackCaptor;
+    @Captor private ArgumentCaptor<ShadeStateEventsListener> mNotifPanelEventsCallbackCaptor;
     @Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor;
 
     private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -84,7 +85,7 @@
 
     private WakefulnessLifecycle.Observer mWakefulnessObserver;
     private StatusBarStateController.StateListener mStatusBarStateListener;
-    private NotifPanelEvents.Listener mNotifPanelEventsCallback;
+    private ShadeStateEvents.ShadeStateEventsListener mNotifPanelEventsCallback;
     private NotifStabilityManager mNotifStabilityManager;
     private NotificationEntry mEntry;
     private GroupEntry mGroupEntry;
@@ -97,7 +98,7 @@
                 mFakeExecutor,
                 mDumpManager,
                 mHeadsUpManager,
-                mNotifPanelEvents,
+                mShadeStateEvents,
                 mStatusBarStateController,
                 mVisualStabilityProvider,
                 mWakefulnessLifecycle);
@@ -111,7 +112,8 @@
         verify(mStatusBarStateController).addCallback(mSBStateListenerCaptor.capture());
         mStatusBarStateListener = mSBStateListenerCaptor.getValue();
 
-        verify(mNotifPanelEvents).registerListener(mNotifPanelEventsCallbackCaptor.capture());
+        verify(mShadeStateEvents).addShadeStateEventsListener(
+                mNotifPanelEventsCallbackCaptor.capture());
         mNotifPanelEventsCallback = mNotifPanelEventsCallbackCaptor.getValue();
 
         verify(mNotifPipeline).setVisualStabilityManager(mNotifStabilityManagerCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
deleted file mode 100644
index ab71264..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.render;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import android.content.Context;
-import android.testing.AndroidTestingRunner;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class ShadeViewDifferTest extends SysuiTestCase {
-    private ShadeViewDiffer mDiffer;
-
-    private FakeController mRootController = new FakeController(mContext, "RootController");
-    private FakeController mController1 = new FakeController(mContext, "Controller1");
-    private FakeController mController2 = new FakeController(mContext, "Controller2");
-    private FakeController mController3 = new FakeController(mContext, "Controller3");
-    private FakeController mController4 = new FakeController(mContext, "Controller4");
-    private FakeController mController5 = new FakeController(mContext, "Controller5");
-    private FakeController mController6 = new FakeController(mContext, "Controller6");
-    private FakeController mController7 = new FakeController(mContext, "Controller7");
-
-    @Mock
-    ShadeViewDifferLogger mLogger;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mDiffer = new ShadeViewDiffer(mRootController, mLogger);
-    }
-
-    @Test
-    public void testAddInitialViews() {
-        // WHEN a spec is applied to an empty root
-        // THEN the final tree matches the spec
-        applySpecAndCheck(
-                node(mController1),
-                node(mController2,
-                        node(mController3),
-                        node(mController4)
-                ),
-                node(mController5)
-        );
-    }
-
-    @Test
-    public void testDetachViews() {
-        // GIVEN a preexisting tree of controllers
-        applySpecAndCheck(
-                node(mController1),
-                node(mController2,
-                        node(mController3),
-                        node(mController4)
-                ),
-                node(mController5)
-        );
-
-        // WHEN the new spec removes nodes
-        // THEN the final tree matches the spec
-        applySpecAndCheck(
-                node(mController5)
-        );
-    }
-
-    @Test
-    public void testReparentChildren() {
-        // GIVEN a preexisting tree of controllers
-        applySpecAndCheck(
-                node(mController1),
-                node(mController2,
-                        node(mController3),
-                        node(mController4)
-                ),
-                node(mController5)
-        );
-
-        // WHEN the parents of the controllers are all shuffled around
-        // THEN the final tree matches the spec
-        applySpecAndCheck(
-                node(mController1),
-                node(mController4),
-                node(mController3,
-                        node(mController2)
-                )
-        );
-    }
-
-    @Test
-    public void testReorderChildren() {
-        // GIVEN a preexisting tree of controllers
-        applySpecAndCheck(
-                node(mController1),
-                node(mController2),
-                node(mController3),
-                node(mController4)
-        );
-
-        // WHEN the children change order
-        // THEN the final tree matches the spec
-        applySpecAndCheck(
-                node(mController3),
-                node(mController2),
-                node(mController4),
-                node(mController1)
-        );
-    }
-
-    @Test
-    public void testRemovedGroupsAreBrokenApart() {
-        // GIVEN a preexisting tree with a group
-        applySpecAndCheck(
-                node(mController1),
-                node(mController2,
-                        node(mController3),
-                        node(mController4),
-                        node(mController5)
-                )
-        );
-
-        // WHEN the new spec removes the entire group
-        applySpecAndCheck(
-                node(mController1)
-        );
-
-        // THEN the group children are no longer attached to their parent
-        assertNull(mController3.getView().getParent());
-        assertNull(mController4.getView().getParent());
-        assertNull(mController5.getView().getParent());
-    }
-
-    @Test
-    public void testUnmanagedViews() {
-        // GIVEN a preexisting tree of controllers
-        applySpecAndCheck(
-                node(mController1),
-                node(mController2,
-                        node(mController3),
-                        node(mController4)
-                ),
-                node(mController5)
-        );
-
-        // GIVEN some additional unmanaged views attached to the tree
-        View unmanagedView1 = new View(mContext);
-        View unmanagedView2 = new View(mContext);
-
-        mRootController.getView().addView(unmanagedView1, 1);
-        mController2.getView().addView(unmanagedView2, 0);
-
-        // WHEN a new spec is applied with additional nodes
-        // THEN the final tree matches the spec
-        applySpecAndCheck(
-                node(mController1),
-                node(mController2,
-                        node(mController3),
-                        node(mController4),
-                        node(mController6)
-                ),
-                node(mController5),
-                node(mController7)
-        );
-
-        // THEN the unmanaged views have been pushed to the end of their parents
-        assertEquals(unmanagedView1, mRootController.view.getChildAt(4));
-        assertEquals(unmanagedView2, mController2.view.getChildAt(3));
-    }
-
-    private void applySpecAndCheck(NodeSpec spec) {
-        mDiffer.applySpec(spec);
-        checkMatchesSpec(spec);
-    }
-
-    private void applySpecAndCheck(SpecBuilder... children) {
-        applySpecAndCheck(node(mRootController, children).build());
-    }
-
-    private void checkMatchesSpec(NodeSpec spec) {
-        final NodeController parent = spec.getController();
-        final List<NodeSpec> children = spec.getChildren();
-
-        for (int i = 0; i < children.size(); i++) {
-            NodeSpec childSpec = children.get(i);
-            View view = parent.getChildAt(i);
-
-            assertEquals(
-                    "Child " + i + " of parent " + parent.getNodeLabel() + " should be "
-                            + childSpec.getController().getNodeLabel() + " but is instead "
-                            + (view != null ? mDiffer.getViewLabel(view) : "null"),
-                    view,
-                    childSpec.getController().getView());
-
-            if (!childSpec.getChildren().isEmpty()) {
-                checkMatchesSpec(childSpec);
-            }
-        }
-    }
-
-    private static class FakeController implements NodeController {
-
-        public final FrameLayout view;
-        private final String mLabel;
-
-        FakeController(Context context, String label) {
-            view = new FrameLayout(context);
-            mLabel = label;
-        }
-
-        @NonNull
-        @Override
-        public String getNodeLabel() {
-            return mLabel;
-        }
-
-        @NonNull
-        @Override
-        public FrameLayout getView() {
-            return view;
-        }
-
-        @Override
-        public int getChildCount() {
-            return view.getChildCount();
-        }
-
-        @Override
-        public View getChildAt(int index) {
-            return view.getChildAt(index);
-        }
-
-        @Override
-        public void addChildAt(@NonNull NodeController child, int index) {
-            view.addView(child.getView(), index);
-        }
-
-        @Override
-        public void moveChildTo(@NonNull NodeController child, int index) {
-            view.removeView(child.getView());
-            view.addView(child.getView(), index);
-        }
-
-        @Override
-        public void removeChild(@NonNull NodeController child, boolean isTransfer) {
-            view.removeView(child.getView());
-        }
-
-        @Override
-        public void onViewAdded() {
-        }
-
-        @Override
-        public void onViewMoved() {
-        }
-
-        @Override
-        public void onViewRemoved() {
-        }
-    }
-
-    private static class SpecBuilder {
-        private final NodeController mController;
-        private final SpecBuilder[] mChildren;
-
-        SpecBuilder(NodeController controller, SpecBuilder... children) {
-            mController = controller;
-            mChildren = children;
-        }
-
-        public NodeSpec build() {
-            return build(null);
-        }
-
-        public NodeSpec build(@Nullable NodeSpec parent) {
-            final NodeSpecImpl spec = new NodeSpecImpl(parent, mController);
-            for (SpecBuilder childBuilder : mChildren) {
-                spec.getChildren().add(childBuilder.build(spec));
-            }
-            return spec;
-        }
-    }
-
-    private static SpecBuilder node(NodeController controller, SpecBuilder... children) {
-        return new SpecBuilder(controller, children);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
new file mode 100644
index 0000000..15cf17d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.render
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ShadeViewDifferTest : SysuiTestCase() {
+    private lateinit var differ: ShadeViewDiffer
+    private val rootController = FakeController(mContext, "RootController")
+    private val controller1 = FakeController(mContext, "Controller1")
+    private val controller2 = FakeController(mContext, "Controller2")
+    private val controller3 = FakeController(mContext, "Controller3")
+    private val controller4 = FakeController(mContext, "Controller4")
+    private val controller5 = FakeController(mContext, "Controller5")
+    private val controller6 = FakeController(mContext, "Controller6")
+    private val controller7 = FakeController(mContext, "Controller7")
+    private val logger: ShadeViewDifferLogger = mock()
+
+    @Before
+    fun setUp() {
+        differ = ShadeViewDiffer(rootController, logger)
+    }
+
+    @Test
+    fun testAddInitialViews() {
+        // WHEN a spec is applied to an empty root
+        // THEN the final tree matches the spec
+        applySpecAndCheck(
+            node(controller1),
+            node(controller2, node(controller3), node(controller4)),
+            node(controller5)
+        )
+    }
+
+    @Test
+    fun testDetachViews() {
+        // GIVEN a preexisting tree of controllers
+        applySpecAndCheck(
+            node(controller1),
+            node(controller2, node(controller3), node(controller4)),
+            node(controller5)
+        )
+
+        // WHEN the new spec removes nodes
+        // THEN the final tree matches the spec
+        applySpecAndCheck(node(controller5))
+    }
+
+    @Test
+    fun testReparentChildren() {
+        // GIVEN a preexisting tree of controllers
+        applySpecAndCheck(
+            node(controller1),
+            node(controller2, node(controller3), node(controller4)),
+            node(controller5)
+        )
+
+        // WHEN the parents of the controllers are all shuffled around
+        // THEN the final tree matches the spec
+        applySpecAndCheck(
+            node(controller1),
+            node(controller4),
+            node(controller3, node(controller2))
+        )
+    }
+
+    @Test
+    fun testReorderChildren() {
+        // GIVEN a preexisting tree of controllers
+        applySpecAndCheck(
+            node(controller1),
+            node(controller2),
+            node(controller3),
+            node(controller4)
+        )
+
+        // WHEN the children change order
+        // THEN the final tree matches the spec
+        applySpecAndCheck(
+            node(controller3),
+            node(controller2),
+            node(controller4),
+            node(controller1)
+        )
+    }
+
+    @Test
+    fun testRemovedGroupsAreBrokenApart() {
+        // GIVEN a preexisting tree with a group
+        applySpecAndCheck(
+            node(controller1),
+            node(controller2, node(controller3), node(controller4), node(controller5))
+        )
+
+        // WHEN the new spec removes the entire group
+        applySpecAndCheck(node(controller1))
+
+        // THEN the group children are no longer attached to their parent
+        Assert.assertNull(controller3.view.parent)
+        Assert.assertNull(controller4.view.parent)
+        Assert.assertNull(controller5.view.parent)
+    }
+
+    @Test
+    fun testUnmanagedViews() {
+        // GIVEN a preexisting tree of controllers
+        applySpecAndCheck(
+            node(controller1),
+            node(controller2, node(controller3), node(controller4)),
+            node(controller5)
+        )
+
+        // GIVEN some additional unmanaged views attached to the tree
+        val unmanagedView1 = View(mContext)
+        val unmanagedView2 = View(mContext)
+        rootController.view.addView(unmanagedView1, 1)
+        controller2.view.addView(unmanagedView2, 0)
+
+        // WHEN a new spec is applied with additional nodes
+        // THEN the final tree matches the spec
+        applySpecAndCheck(
+            node(controller1),
+            node(controller2, node(controller3), node(controller4), node(controller6)),
+            node(controller5),
+            node(controller7)
+        )
+
+        // THEN the unmanaged views have been pushed to the end of their parents
+        Assert.assertEquals(unmanagedView1, rootController.view.getChildAt(4))
+        Assert.assertEquals(unmanagedView2, controller2.view.getChildAt(3))
+    }
+
+    private fun applySpecAndCheck(spec: NodeSpec) {
+        differ.applySpec(spec)
+        checkMatchesSpec(spec)
+    }
+
+    private fun applySpecAndCheck(vararg children: SpecBuilder) {
+        applySpecAndCheck(node(rootController, *children).build())
+    }
+
+    private fun checkMatchesSpec(spec: NodeSpec) {
+        val parent = spec.controller
+        val children = spec.children
+        for (i in children.indices) {
+            val childSpec = children[i]
+            val view = parent.getChildAt(i)
+            Assert.assertEquals(
+                "Child $i of parent ${parent.nodeLabel} " +
+                    "should be ${childSpec.controller.nodeLabel} " +
+                    "but instead " +
+                    view?.let(differ::getViewLabel),
+                view,
+                childSpec.controller.view
+            )
+            if (childSpec.children.isNotEmpty()) {
+                checkMatchesSpec(childSpec)
+            }
+        }
+    }
+
+    private class FakeController(context: Context, label: String) : NodeController {
+        override val view: FrameLayout = FrameLayout(context)
+        override val nodeLabel: String = label
+        override fun getChildCount(): Int = view.childCount
+
+        override fun getChildAt(index: Int): View? {
+            return view.getChildAt(index)
+        }
+
+        override fun addChildAt(child: NodeController, index: Int) {
+            view.addView(child.view, index)
+        }
+
+        override fun moveChildTo(child: NodeController, index: Int) {
+            view.removeView(child.view)
+            view.addView(child.view, index)
+        }
+
+        override fun removeChild(child: NodeController, isTransfer: Boolean) {
+            view.removeView(child.view)
+        }
+
+        override fun onViewAdded() {}
+        override fun onViewMoved() {}
+        override fun onViewRemoved() {}
+    }
+
+    private class SpecBuilder(
+        private val mController: NodeController,
+        private val children: Array<out SpecBuilder>
+    ) {
+
+        @JvmOverloads
+        fun build(parent: NodeSpec? = null): NodeSpec {
+            val spec = NodeSpecImpl(parent, mController)
+            for (childBuilder in children) {
+                spec.children.add(childBuilder.build(spec))
+            }
+            return spec
+        }
+    }
+
+    companion object {
+        private fun node(controller: NodeController, vararg children: SpecBuilder): SpecBuilder {
+            return SpecBuilder(controller, children)
+        }
+    }
+}
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 91aecd8..dceb4ff 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
@@ -78,6 +78,7 @@
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -89,6 +90,7 @@
 /**
  * Tests for {@link NotificationStackScrollLayout}.
  */
+@Ignore("b/255552856")
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index 6ff7b7c..de1fec8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -24,7 +24,14 @@
     private val _subscriptionsModelFlow = MutableStateFlow(MobileSubscriptionModel())
     override val subscriptionModelFlow: Flow<MobileSubscriptionModel> = _subscriptionsModelFlow
 
+    private val _dataEnabled = MutableStateFlow(true)
+    override val dataEnabled = _dataEnabled
+
     fun setMobileSubscriptionModel(model: MobileSubscriptionModel) {
         _subscriptionsModelFlow.value = model
     }
+
+    fun setDataEnabled(enabled: Boolean) {
+        _dataEnabled.value = enabled
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index c88d468..813e750 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -50,7 +50,7 @@
         _activeMobileDataSubscriptionId.value = subId
     }
 
-    fun setMobileConnectionRepositoryForId(subId: Int, repo: MobileConnectionRepository) {
-        subIdRepos[subId] = repo
+    fun setMobileConnectionRepositoryMap(connections: Map<Int, MobileConnectionRepository>) {
+        connections.forEach { entry -> subIdRepos[entry.key] = entry.value }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepositoryTest.kt
index 775e6db..0939364 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepositoryTest.kt
@@ -20,16 +20,20 @@
 import android.telephony.ServiceState
 import android.telephony.SignalStrength
 import android.telephony.SubscriptionInfo
-import android.telephony.SubscriptionManager
 import android.telephony.TelephonyCallback
 import android.telephony.TelephonyCallback.ServiceStateListener
 import android.telephony.TelephonyDisplayInfo
 import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA
 import android.telephony.TelephonyManager
+import android.telephony.TelephonyManager.DATA_CONNECTED
+import android.telephony.TelephonyManager.DATA_CONNECTING
+import android.telephony.TelephonyManager.DATA_DISCONNECTED
+import android.telephony.TelephonyManager.DATA_DISCONNECTING
 import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DefaultNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType
@@ -59,7 +63,6 @@
 class MobileConnectionRepositoryTest : SysuiTestCase() {
     private lateinit var underTest: MobileConnectionRepositoryImpl
 
-    @Mock private lateinit var subscriptionManager: SubscriptionManager
     @Mock private lateinit var telephonyManager: TelephonyManager
     @Mock private lateinit var logger: ConnectivityPipelineLogger
 
@@ -148,16 +151,61 @@
         }
 
     @Test
-    fun testFlowForSubId_dataConnectionState() =
+    fun testFlowForSubId_dataConnectionState_connected() =
         runBlocking(IMMEDIATE) {
             var latest: MobileSubscriptionModel? = null
             val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
 
             val callback =
                 getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
-            callback.onDataConnectionStateChanged(100, 200 /* unused */)
+            callback.onDataConnectionStateChanged(DATA_CONNECTED, 200 /* unused */)
 
-            assertThat(latest?.dataConnectionState).isEqualTo(100)
+            assertThat(latest?.dataConnectionState).isEqualTo(DataConnectionState.Connected)
+
+            job.cancel()
+        }
+
+    @Test
+    fun testFlowForSubId_dataConnectionState_connecting() =
+        runBlocking(IMMEDIATE) {
+            var latest: MobileSubscriptionModel? = null
+            val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
+
+            val callback =
+                getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
+            callback.onDataConnectionStateChanged(DATA_CONNECTING, 200 /* unused */)
+
+            assertThat(latest?.dataConnectionState).isEqualTo(DataConnectionState.Connecting)
+
+            job.cancel()
+        }
+
+    @Test
+    fun testFlowForSubId_dataConnectionState_disconnected() =
+        runBlocking(IMMEDIATE) {
+            var latest: MobileSubscriptionModel? = null
+            val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
+
+            val callback =
+                getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
+            callback.onDataConnectionStateChanged(DATA_DISCONNECTED, 200 /* unused */)
+
+            assertThat(latest?.dataConnectionState).isEqualTo(DataConnectionState.Disconnected)
+
+            job.cancel()
+        }
+
+    @Test
+    fun testFlowForSubId_dataConnectionState_disconnecting() =
+        runBlocking(IMMEDIATE) {
+            var latest: MobileSubscriptionModel? = null
+            val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
+
+            val callback =
+                getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
+            callback.onDataConnectionStateChanged(DATA_DISCONNECTING, 200 /* unused */)
+
+            assertThat(latest?.dataConnectionState).isEqualTo(DataConnectionState.Disconnecting)
 
             job.cancel()
         }
@@ -241,6 +289,32 @@
             job.cancel()
         }
 
+    @Test
+    fun dataEnabled_isEnabled() =
+        runBlocking(IMMEDIATE) {
+            whenever(telephonyManager.isDataConnectionAllowed).thenReturn(true)
+
+            var latest: Boolean? = null
+            val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun dataEnabled_isDisabled() =
+        runBlocking(IMMEDIATE) {
+            whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
+
+            var latest: Boolean? = null
+            val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
     private fun getTelephonyCallbacks(): List<TelephonyCallback> {
         val callbackCaptor = argumentCaptor<TelephonyCallback>()
         Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index cd4dbeb..5611c44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -22,19 +22,22 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 
 class FakeMobileIconInteractor : MobileIconInteractor {
-    private val _iconGroup = MutableStateFlow<SignalIcon.MobileIconGroup>(TelephonyIcons.UNKNOWN)
+    private val _iconGroup = MutableStateFlow<SignalIcon.MobileIconGroup>(TelephonyIcons.THREE_G)
     override val networkTypeIconGroup = _iconGroup
 
-    private val _isEmergencyOnly = MutableStateFlow<Boolean>(false)
+    private val _isEmergencyOnly = MutableStateFlow(false)
     override val isEmergencyOnly = _isEmergencyOnly
 
-    private val _level = MutableStateFlow<Int>(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
+    private val _isDataEnabled = MutableStateFlow(true)
+    override val isDataEnabled = _isDataEnabled
+
+    private val _level = MutableStateFlow(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
     override val level = _level
 
-    private val _numberOfLevels = MutableStateFlow<Int>(4)
+    private val _numberOfLevels = MutableStateFlow(4)
     override val numberOfLevels = _numberOfLevels
 
-    private val _cutOut = MutableStateFlow<Boolean>(false)
+    private val _cutOut = MutableStateFlow(false)
     override val cutOut = _cutOut
 
     fun setIconGroup(group: SignalIcon.MobileIconGroup) {
@@ -45,6 +48,10 @@
         _isEmergencyOnly.value = emergency
     }
 
+    fun setIsDataEnabled(enabled: Boolean) {
+        _isDataEnabled.value = enabled
+    }
+
     fun setLevel(level: Int) {
         _level.value = level
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index b01efd1..877ce0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -19,6 +19,7 @@
 import android.telephony.SubscriptionInfo
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -41,7 +42,7 @@
 class MobileIconsInteractorTest : SysuiTestCase() {
     private lateinit var underTest: MobileIconsInteractor
     private val userSetupRepository = FakeUserSetupRepository()
-    private val subscriptionsRepository = FakeMobileConnectionsRepository()
+    private val connectionsRepository = FakeMobileConnectionsRepository()
     private val mobileMappingsProxy = FakeMobileMappingsProxy()
     private val scope = CoroutineScope(IMMEDIATE)
 
@@ -50,9 +51,20 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+
+        connectionsRepository.setMobileConnectionRepositoryMap(
+            mapOf(
+                SUB_1_ID to CONNECTION_1,
+                SUB_2_ID to CONNECTION_2,
+                SUB_3_ID to CONNECTION_3,
+                SUB_4_ID to CONNECTION_4,
+            )
+        )
+        connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
+
         underTest =
             MobileIconsInteractorImpl(
-                subscriptionsRepository,
+                connectionsRepository,
                 carrierConfigTracker,
                 mobileMappingsProxy,
                 userSetupRepository,
@@ -76,7 +88,7 @@
     @Test
     fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
         runBlocking(IMMEDIATE) {
-            subscriptionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
 
             var latest: List<SubscriptionInfo>? = null
             val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
@@ -89,8 +101,8 @@
     @Test
     fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_3() =
         runBlocking(IMMEDIATE) {
-            subscriptionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
-            subscriptionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+            connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(false)
 
@@ -106,8 +118,8 @@
     @Test
     fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_4() =
         runBlocking(IMMEDIATE) {
-            subscriptionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
-            subscriptionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
+            connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(false)
 
@@ -123,8 +135,8 @@
     @Test
     fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_active_1() =
         runBlocking(IMMEDIATE) {
-            subscriptionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
-            subscriptionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
+            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(true)
 
@@ -141,8 +153,8 @@
     @Test
     fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_nonActive_1() =
         runBlocking(IMMEDIATE) {
-            subscriptionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
-            subscriptionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(true)
 
@@ -162,10 +174,12 @@
         private const val SUB_1_ID = 1
         private val SUB_1 =
             mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
+        private val CONNECTION_1 = FakeMobileConnectionRepository()
 
         private const val SUB_2_ID = 2
         private val SUB_2 =
             mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
+        private val CONNECTION_2 = FakeMobileConnectionRepository()
 
         private const val SUB_3_ID = 3
         private val SUB_3_OPP =
@@ -173,6 +187,7 @@
                 whenever(it.subscriptionId).thenReturn(SUB_3_ID)
                 whenever(it.isOpportunistic).thenReturn(true)
             }
+        private val CONNECTION_3 = FakeMobileConnectionRepository()
 
         private const val SUB_4_ID = 4
         private val SUB_4_OPP =
@@ -180,5 +195,6 @@
                 whenever(it.subscriptionId).thenReturn(SUB_4_ID)
                 whenever(it.isOpportunistic).thenReturn(true)
             }
+        private val CONNECTION_4 = FakeMobileConnectionRepository()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index b374abb..ce0f33f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -18,8 +18,10 @@
 
 import androidx.test.filters.SmallTest
 import com.android.settingslib.graph.SignalDrawable
-import com.android.settingslib.mobile.TelephonyIcons
+import com.android.settingslib.mobile.TelephonyIcons.THREE_G
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.google.common.truth.Truth.assertThat
@@ -27,6 +29,7 @@
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
 import org.junit.Before
 import org.junit.Test
 import org.mockito.Mock
@@ -44,7 +47,7 @@
         interactor.apply {
             setLevel(1)
             setCutOut(false)
-            setIconGroup(TelephonyIcons.THREE_G)
+            setIconGroup(THREE_G)
             setIsEmergencyOnly(false)
             setNumberOfLevels(4)
         }
@@ -62,6 +65,60 @@
             job.cancel()
         }
 
+    @Test
+    fun networkType_dataEnabled_groupIsRepresented() =
+        runBlocking(IMMEDIATE) {
+            val expected =
+                Icon.Resource(
+                    THREE_G.dataType,
+                    ContentDescription.Resource(THREE_G.dataContentDescription)
+                )
+            interactor.setIconGroup(THREE_G)
+
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(expected)
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_nullWhenDisabled() =
+        runBlocking(IMMEDIATE) {
+            interactor.setIconGroup(THREE_G)
+            interactor.setIsDataEnabled(false)
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_null_changeToDisabled() =
+        runBlocking(IMMEDIATE) {
+            val expected =
+                Icon.Resource(
+                    THREE_G.dataType,
+                    ContentDescription.Resource(THREE_G.dataContentDescription)
+                )
+            interactor.setIconGroup(THREE_G)
+            interactor.setIsDataEnabled(true)
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(expected)
+
+            interactor.setIsDataEnabled(false)
+            yield()
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
         private const val SUB_1_ID = 1
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index b68eb88..91b5c35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -41,6 +41,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
@@ -85,10 +86,29 @@
     }
 
     @Test
-    fun displayView_viewAdded() {
-        underTest.displayView(getState())
+    fun displayView_viewAddedWithCorrectTitle() {
+        underTest.displayView(
+            ViewInfo(
+                name = "name",
+                windowTitle = "Fake Window Title",
+            )
+        )
 
-        verify(windowManager).addView(any(), any())
+        val windowParamsCaptor = argumentCaptor<WindowManager.LayoutParams>()
+        verify(windowManager).addView(any(), capture(windowParamsCaptor))
+        assertThat(windowParamsCaptor.value!!.title).isEqualTo("Fake Window Title")
+    }
+
+    @Test
+    fun displayView_logged() {
+        underTest.displayView(
+            ViewInfo(
+                name = "name",
+                windowTitle = "Fake Window Title",
+            )
+        )
+
+        verify(logger).logViewAddition("Fake Window Title")
     }
 
     @Test
@@ -110,7 +130,7 @@
     }
 
     @Test
-    fun displayView_twice_viewNotAddedTwice() {
+    fun displayView_twiceWithSameWindowTitle_viewNotAddedTwice() {
         underTest.displayView(getState())
         reset(windowManager)
 
@@ -119,6 +139,32 @@
     }
 
     @Test
+    fun displayView_twiceWithDifferentWindowTitles_oldViewRemovedNewViewAdded() {
+        underTest.displayView(
+            ViewInfo(
+                name = "name",
+                windowTitle = "First Fake Window Title",
+            )
+        )
+
+        underTest.displayView(
+            ViewInfo(
+                name = "name",
+                windowTitle = "Second Fake Window Title",
+            )
+        )
+
+        val viewCaptor = argumentCaptor<View>()
+        val windowParamsCaptor = argumentCaptor<WindowManager.LayoutParams>()
+
+        verify(windowManager, times(2)).addView(capture(viewCaptor), capture(windowParamsCaptor))
+
+        assertThat(windowParamsCaptor.allValues[0].title).isEqualTo("First Fake Window Title")
+        assertThat(windowParamsCaptor.allValues[1].title).isEqualTo("Second Fake Window Title")
+        verify(windowManager).removeView(viewCaptor.allValues[0])
+    }
+
+    @Test
     fun displayView_viewDoesNotDisappearsBeforeTimeout() {
         val state = getState()
         underTest.displayView(state)
@@ -197,7 +243,7 @@
         underTest.removeView(reason)
 
         verify(windowManager).removeView(any())
-        verify(logger).logChipRemoval(reason)
+        verify(logger).logViewRemoval(reason)
     }
 
     @Test
@@ -232,8 +278,6 @@
         configurationController,
         powerManager,
         R.layout.chipbar,
-        "Window Title",
-        "WAKE_REASON",
     ) {
         var mostRecentViewInfo: ViewInfo? = null
 
@@ -250,9 +294,12 @@
         }
     }
 
-    inner class ViewInfo(val name: String) : TemporaryViewInfo {
-        override fun getTimeoutMs() = 1L
-    }
+    inner class ViewInfo(
+        val name: String,
+        override val windowTitle: String = "Window Title",
+        override val wakeReason: String = "WAKE_REASON",
+        override val timeoutMs: Int = 1
+    ) : TemporaryViewInfo()
 }
 
 private const val TIMEOUT_MS = 10000L
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
index 13e9f60..d155050 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
@@ -43,20 +43,21 @@
     }
 
     @Test
-    fun logChipAddition_bufferHasLog() {
-        logger.logChipAddition()
+    fun logViewAddition_bufferHasLog() {
+        logger.logViewAddition("Test Window Title")
 
         val stringWriter = StringWriter()
         buffer.dump(PrintWriter(stringWriter), tailLength = 0)
         val actualString = stringWriter.toString()
 
         assertThat(actualString).contains(TAG)
+        assertThat(actualString).contains("Test Window Title")
     }
 
     @Test
-    fun logChipRemoval_bufferHasTagAndReason() {
+    fun logViewRemoval_bufferHasTagAndReason() {
         val reason = "test reason"
-        logger.logChipRemoval(reason)
+        logger.logViewRemoval(reason)
 
         val stringWriter = StringWriter()
         buffer.dump(PrintWriter(stringWriter), tailLength = 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 9fbf159..f643973 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -35,12 +35,12 @@
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.systemui.util.view.ViewUtil
 import com.google.common.truth.Truth.assertThat
@@ -60,7 +60,7 @@
 class ChipbarCoordinatorTest : SysuiTestCase() {
     private lateinit var underTest: FakeChipbarCoordinator
 
-    @Mock private lateinit var logger: MediaTttLogger
+    @Mock private lateinit var logger: ChipbarLogger
     @Mock private lateinit var accessibilityManager: AccessibilityManager
     @Mock private lateinit var configurationController: ConfigurationController
     @Mock private lateinit var powerManager: PowerManager
@@ -105,7 +105,7 @@
         val drawable = context.getDrawable(R.drawable.ic_celebration)!!
 
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Loaded(drawable, contentDescription = ContentDescription.Loaded("loadedCD")),
                 Text.Loaded("text"),
                 endItem = null,
@@ -121,7 +121,7 @@
     fun displayView_resourceIcon_correctlyRendered() {
         val contentDescription = ContentDescription.Resource(R.string.controls_error_timeout)
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Resource(R.drawable.ic_cake, contentDescription),
                 Text.Loaded("text"),
                 endItem = null,
@@ -136,7 +136,7 @@
     @Test
     fun displayView_loadedText_correctlyRendered() {
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Resource(R.id.check_box, null),
                 Text.Loaded("display view text here"),
                 endItem = null,
@@ -149,7 +149,7 @@
     @Test
     fun displayView_resourceText_correctlyRendered() {
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Resource(R.id.check_box, null),
                 Text.Resource(R.string.screenrecord_start_error),
                 endItem = null,
@@ -163,7 +163,7 @@
     @Test
     fun displayView_endItemNull_correctlyRendered() {
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Resource(R.id.check_box, null),
                 Text.Loaded("text"),
                 endItem = null,
@@ -179,7 +179,7 @@
     @Test
     fun displayView_endItemLoading_correctlyRendered() {
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Resource(R.id.check_box, null),
                 Text.Loaded("text"),
                 endItem = ChipbarEndItem.Loading,
@@ -195,7 +195,7 @@
     @Test
     fun displayView_endItemError_correctlyRendered() {
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Resource(R.id.check_box, null),
                 Text.Loaded("text"),
                 endItem = ChipbarEndItem.Error,
@@ -211,7 +211,7 @@
     @Test
     fun displayView_endItemButton_correctlyRendered() {
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Resource(R.id.check_box, null),
                 Text.Loaded("text"),
                 endItem =
@@ -237,7 +237,7 @@
         val buttonClickListener = View.OnClickListener { isClicked = true }
 
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Resource(R.id.check_box, null),
                 Text.Loaded("text"),
                 endItem =
@@ -260,7 +260,7 @@
         val buttonClickListener = View.OnClickListener { isClicked = true }
 
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Resource(R.id.check_box, null),
                 Text.Loaded("text"),
                 endItem =
@@ -279,7 +279,7 @@
     @Test
     fun displayView_vibrationEffect_doubleClickEffect() {
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Resource(R.id.check_box, null),
                 Text.Loaded("text"),
                 endItem = null,
@@ -296,7 +296,7 @@
         val drawable = context.getDrawable(R.drawable.ic_celebration)!!
 
         underTest.displayView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Loaded(drawable, contentDescription = ContentDescription.Loaded("loadedCD")),
                 Text.Loaded("title text"),
                 endItem = ChipbarEndItem.Loading,
@@ -314,7 +314,7 @@
         // WHEN the view is updated
         val newDrawable = context.getDrawable(R.drawable.ic_cake)!!
         underTest.updateView(
-            ChipbarInfo(
+            createChipbarInfo(
                 Icon.Loaded(newDrawable, ContentDescription.Loaded("new CD")),
                 Text.Loaded("new title text"),
                 endItem = ChipbarEndItem.Error,
@@ -331,6 +331,47 @@
         assertThat(chipbarView.getEndButton().visibility).isEqualTo(View.GONE)
     }
 
+    @Test
+    fun viewUpdates_logged() {
+        val drawable = context.getDrawable(R.drawable.ic_celebration)!!
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Loaded(drawable, contentDescription = ContentDescription.Loaded("loadedCD")),
+                Text.Loaded("title text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        verify(logger).logViewUpdate(eq(WINDOW_TITLE), eq("title text"), any())
+
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Loaded(drawable, ContentDescription.Loaded("new CD")),
+                Text.Loaded("new title text"),
+                endItem = ChipbarEndItem.Error,
+            )
+        )
+
+        verify(logger).logViewUpdate(eq(WINDOW_TITLE), eq("new title text"), any())
+    }
+
+    private fun createChipbarInfo(
+        startIcon: Icon,
+        text: Text,
+        endItem: ChipbarEndItem?,
+        vibrationEffect: VibrationEffect? = null,
+    ): ChipbarInfo {
+        return ChipbarInfo(
+            startIcon,
+            text,
+            endItem,
+            vibrationEffect,
+            windowTitle = WINDOW_TITLE,
+            wakeReason = WAKE_REASON,
+            timeoutMs = TIMEOUT,
+        )
+    }
+
     private fun ViewGroup.getStartIconView() = this.requireViewById<ImageView>(R.id.start_icon)
 
     private fun ViewGroup.getChipText(): String =
@@ -350,3 +391,5 @@
 }
 
 private const val TIMEOUT = 10000
+private const val WINDOW_TITLE = "Test Chipbar Window Title"
+private const val WAKE_REASON = "TEST_CHIPBAR_WAKE_REASON"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
index 17d4023..574f70e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
@@ -22,8 +22,6 @@
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
-import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -33,7 +31,7 @@
 /** A fake implementation of [ChipbarCoordinator] for testing. */
 class FakeChipbarCoordinator(
     context: Context,
-    @MediaTttReceiverLogger logger: MediaTttLogger,
+    logger: ChipbarLogger,
     windowManager: WindowManager,
     mainExecutor: DelayableExecutor,
     accessibilityManager: AccessibilityManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
index 120bf79..e496521 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
@@ -219,6 +219,7 @@
             repository.setUserInfos(listOf(NON_GUEST_USER_INFO, EPHEMERAL_GUEST_USER_INFO))
             repository.setSelectedUserInfo(EPHEMERAL_GUEST_USER_INFO)
             val targetUserId = NON_GUEST_USER_INFO.id
+            val ephemeralGuestUserHandle = UserHandle.of(EPHEMERAL_GUEST_USER_INFO.id)
 
             underTest.exit(
                 guestUserId = GUEST_USER_INFO.id,
@@ -230,7 +231,7 @@
             )
 
             verify(manager).markGuestForDeletion(EPHEMERAL_GUEST_USER_INFO.id)
-            verify(manager).removeUser(EPHEMERAL_GUEST_USER_INFO.id)
+            verify(manager).removeUserWhenPossible(ephemeralGuestUserHandle, false)
             verify(switchUser).invoke(targetUserId)
         }
 
@@ -240,6 +241,7 @@
             whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
             repository.setSelectedUserInfo(GUEST_USER_INFO)
             val targetUserId = NON_GUEST_USER_INFO.id
+            val guestUserHandle = UserHandle.of(GUEST_USER_INFO.id)
 
             underTest.exit(
                 guestUserId = GUEST_USER_INFO.id,
@@ -251,7 +253,7 @@
             )
 
             verify(manager).markGuestForDeletion(GUEST_USER_INFO.id)
-            verify(manager).removeUser(GUEST_USER_INFO.id)
+            verify(manager).removeUserWhenPossible(guestUserHandle, false)
             verify(switchUser).invoke(targetUserId)
         }
 
@@ -296,6 +298,7 @@
             repository.setSelectedUserInfo(GUEST_USER_INFO)
 
             val targetUserId = NON_GUEST_USER_INFO.id
+            val guestUserHandle = UserHandle.of(GUEST_USER_INFO.id)
             underTest.remove(
                 guestUserId = GUEST_USER_INFO.id,
                 targetUserId = targetUserId,
@@ -305,7 +308,7 @@
             )
 
             verify(manager).markGuestForDeletion(GUEST_USER_INFO.id)
-            verify(manager).removeUser(GUEST_USER_INFO.id)
+            verify(manager).removeUserWhenPossible(guestUserHandle, false)
             verify(switchUser).invoke(targetUserId)
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index c254358..379bb28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -26,8 +26,8 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -44,6 +44,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.Display;
@@ -135,9 +136,10 @@
         when(mWallpaperBitmap.getHeight()).thenReturn(mBitmapHeight);
 
         // set up wallpaper manager
-        when(mWallpaperManager.peekBitmapDimensions()).thenReturn(
-                new Rect(0, 0, mBitmapWidth, mBitmapHeight));
-        when(mWallpaperManager.getBitmap(false)).thenReturn(mWallpaperBitmap);
+        when(mWallpaperManager.peekBitmapDimensions())
+                .thenReturn(new Rect(0, 0, mBitmapWidth, mBitmapHeight));
+        when(mWallpaperManager.getBitmapAsUser(eq(UserHandle.USER_CURRENT), anyBoolean()))
+                .thenReturn(mWallpaperBitmap);
         when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(mWallpaperManager);
 
         // set up surface
@@ -286,9 +288,6 @@
         testMinSurfaceHelper(8, 8);
         testMinSurfaceHelper(100, 2000);
         testMinSurfaceHelper(200, 1);
-        testMinSurfaceHelper(0, 1);
-        testMinSurfaceHelper(1, 0);
-        testMinSurfaceHelper(0, 0);
     }
 
     private void testMinSurfaceHelper(int bitmapWidth, int bitmapHeight) {
@@ -307,28 +306,6 @@
     }
 
     @Test
-    public void testZeroBitmap() {
-        // test that a frame is never drawn with a 0 bitmap
-        testZeroBitmapHelper(0, 1);
-        testZeroBitmapHelper(1, 0);
-        testZeroBitmapHelper(0, 0);
-    }
-
-    private void testZeroBitmapHelper(int bitmapWidth, int bitmapHeight) {
-
-        clearInvocations(mSurfaceHolder);
-        setBitmapDimensions(bitmapWidth, bitmapHeight);
-
-        ImageWallpaper imageWallpaper = createImageWallpaperCanvas();
-        ImageWallpaper.CanvasEngine engine =
-                (ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
-        ImageWallpaper.CanvasEngine spyEngine = spy(engine);
-        spyEngine.onCreate(mSurfaceHolder);
-        spyEngine.onSurfaceRedrawNeeded(mSurfaceHolder);
-        verify(spyEngine, never()).drawFrameOnCanvas(any());
-    }
-
-    @Test
     public void testLoadDrawAndUnloadBitmap() {
         setBitmapDimensions(LOW_BMP_WIDTH, LOW_BMP_HEIGHT);
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
new file mode 100644
index 0000000..96658c6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
@@ -0,0 +1,48 @@
+package com.android.systemui.biometrics.data.repository
+
+import android.hardware.biometrics.PromptInfo
+import com.android.systemui.biometrics.data.model.PromptKind
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Fake implementation of [PromptRepository] for tests. */
+class FakePromptRepository : PromptRepository {
+
+    private val _isShowing = MutableStateFlow(false)
+    override val isShowing = _isShowing.asStateFlow()
+
+    private val _promptInfo = MutableStateFlow<PromptInfo?>(null)
+    override val promptInfo = _promptInfo.asStateFlow()
+
+    private val _userId = MutableStateFlow<Int?>(null)
+    override val userId = _userId.asStateFlow()
+
+    private var _challenge = MutableStateFlow<Long?>(null)
+    override val challenge = _challenge.asStateFlow()
+
+    private val _kind = MutableStateFlow(PromptKind.ANY_BIOMETRIC)
+    override val kind = _kind.asStateFlow()
+
+    override fun setPrompt(
+        promptInfo: PromptInfo,
+        userId: Int,
+        gatekeeperChallenge: Long?,
+        kind: PromptKind
+    ) {
+        _promptInfo.value = promptInfo
+        _userId.value = userId
+        _challenge.value = gatekeeperChallenge
+        _kind.value = kind
+    }
+
+    override fun unsetPrompt() {
+        _promptInfo.value = null
+        _userId.value = null
+        _challenge.value = null
+        _kind.value = PromptKind.ANY_BIOMETRIC
+    }
+
+    fun setIsShowing(showing: Boolean) {
+        _isShowing.value = showing
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt
new file mode 100644
index 0000000..fbe291e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt
@@ -0,0 +1,31 @@
+package com.android.systemui.biometrics.domain.interactor
+
+import com.android.internal.widget.LockscreenCredential
+import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/** Fake implementation of [CredentialInteractor] for tests. */
+class FakeCredentialInteractor : CredentialInteractor {
+
+    /** Sets return value for [isStealthModeActive]. */
+    var stealthMode: Boolean = false
+
+    /** Sets return value for [getCredentialOwnerOrSelfId]. */
+    var credentialOwnerId: Int? = null
+
+    override fun isStealthModeActive(userId: Int): Boolean = stealthMode
+
+    override fun getCredentialOwnerOrSelfId(userId: Int): Int = credentialOwnerId ?: userId
+
+    override fun verifyCredential(
+        request: BiometricPromptRequest.Credential,
+        credential: LockscreenCredential,
+    ): Flow<CredentialStatus> = verifyCredentialResponse(credential)
+
+    /** Sets the result value for [verifyCredential]. */
+    var verifyCredentialResponse: (credential: LockscreenCredential) -> Flow<CredentialStatus> =
+        { _ ->
+            flowOf(CredentialStatus.Fail.Error("invalid"))
+        }
+}
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 0c12680..11178db 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
@@ -19,6 +19,7 @@
 
 import com.android.systemui.common.shared.model.Position
 import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -48,6 +49,9 @@
     private val _statusBarState = MutableStateFlow(StatusBarState.SHADE)
     override val statusBarState: Flow<StatusBarState> = _statusBarState
 
+    private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
+    override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
+
     override fun isKeyguardShowing(): Boolean {
         return _isKeyguardShowing.value
     }
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
new file mode 100644
index 0000000..6c44244
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.keyguard.data.repository
+
+import android.annotation.FloatRange
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import java.util.UUID
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+
+/** Fake implementation of [KeyguardTransitionRepository] */
+class FakeKeyguardTransitionRepository : KeyguardTransitionRepository {
+
+    private val _transitions = MutableSharedFlow<TransitionStep>()
+    override val transitions: SharedFlow<TransitionStep> = _transitions
+
+    suspend fun sendTransitionStep(step: TransitionStep) {
+        _transitions.emit(step)
+    }
+
+    override fun startTransition(info: TransitionInfo): UUID? {
+        return null
+    }
+
+    override fun updateTransition(
+        transitionId: UUID,
+        @FloatRange(from = 0.0, to = 1.0) value: Float,
+        state: TransitionState
+    ) = Unit
+}
diff --git a/packages/VpnDialogs/res/values-es-rUS/strings.xml b/packages/VpnDialogs/res/values-es-rUS/strings.xml
index 108a24e..232b53a 100644
--- a/packages/VpnDialogs/res/values-es-rUS/strings.xml
+++ b/packages/VpnDialogs/res/values-es-rUS/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
-    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN capaz de controlar el tráfico de la red. Acéptala solo si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando se activa la VPN."</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN capaz de supervisar el tráfico de la red. Acéptala solo si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando se activa la VPN."</string>
     <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita supervisar el tráfico de red. Solo acéptala si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá en tu pantalla cuando se active la VPN."</string>
     <string name="legacy_title" msgid="192936250066580964">"La VPN está conectada."</string>
     <string name="session" msgid="6470628549473641030">"Sesión:"</string>
diff --git a/packages/VpnDialogs/res/values-es/strings.xml b/packages/VpnDialogs/res/values-es/strings.xml
index 9bf86f5..4e21fd09 100644
--- a/packages/VpnDialogs/res/values-es/strings.xml
+++ b/packages/VpnDialogs/res/values-es/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
-    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN para controlar el tráfico de red. Solo debes aceptarla si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando se active la conexión VPN."</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN para controlar el tráfico de red. Solo debes aceptarla si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando la conexión VPN está activa."</string>
     <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita monitorizar el tráfico de red. Acéptalo solo si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá en la pantalla cuando la VPN esté activa."</string>
     <string name="legacy_title" msgid="192936250066580964">"La VPN está conectada"</string>
     <string name="session" msgid="6470628549473641030">"Sesión:"</string>
diff --git a/packages/VpnDialogs/res/values-nl/strings.xml b/packages/VpnDialogs/res/values-nl/strings.xml
index 33f8a89..76f56af 100644
--- a/packages/VpnDialogs/res/values-nl/strings.xml
+++ b/packages/VpnDialogs/res/values-nl/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Verbindingsverzoek"</string>
-    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding opzetten om netwerkverkeer te controleren. Accepteer het verzoek alleen als je de bron vertrouwt. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; wordt boven aan je scherm weergegeven wanneer VPN actief is."</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding instellen waarmee de app het netwerkverkeer kan bijhouden. Accepteer dit alleen als je de bron vertrouwt. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verschijnt op je scherm als het VPN actief is."</string>
     <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding instellen waarmee de app het netwerkverkeer kan bijhouden. Accepteer dit alleen als je de bron vertrouwt. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verschijnt op je scherm als het VPN actief is."</string>
     <string name="legacy_title" msgid="192936250066580964">"Verbinding met VPN"</string>
     <string name="session" msgid="6470628549473641030">"Sessie:"</string>
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 fc628cf..000bafe 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -44,6 +44,7 @@
 import android.window.DisplayWindowPolicyController;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.BlockedAppStreamingActivity;
 
 import java.util.List;
@@ -112,7 +113,9 @@
     final ArraySet<Integer> mRunningUids = new ArraySet<>();
     @Nullable private final ActivityListener mActivityListener;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
-    private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListener =
+    @NonNull
+    @GuardedBy("mGenericWindowPolicyControllerLock")
+    private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners =
             new ArraySet<>();
     @Nullable
     private final @AssociationRequest.DeviceProfile String mDeviceProfile;
@@ -178,12 +181,16 @@
 
     /** Register a listener for running applications changes. */
     public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) {
-        mRunningAppsChangedListener.add(listener);
+        synchronized (mGenericWindowPolicyControllerLock) {
+            mRunningAppsChangedListeners.add(listener);
+        }
     }
 
     /** Unregister a listener for running applications changes. */
     public void unregisterRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) {
-        mRunningAppsChangedListener.remove(listener);
+        synchronized (mGenericWindowPolicyControllerLock) {
+            mRunningAppsChangedListeners.remove(listener);
+        }
     }
 
     @Override
@@ -283,12 +290,16 @@
                 // Post callback on the main thread so it doesn't block activity launching
                 mHandler.post(() -> mActivityListener.onDisplayEmpty(mDisplayId));
             }
-        }
-        mHandler.post(() -> {
-            for (RunningAppsChangedListener listener : mRunningAppsChangedListener) {
-                listener.onRunningAppsChanged(runningUids);
+            if (!mRunningAppsChangedListeners.isEmpty()) {
+                final ArraySet<RunningAppsChangedListener> listeners =
+                        new ArraySet<>(mRunningAppsChangedListeners);
+                mHandler.post(() -> {
+                    for (RunningAppsChangedListener listener : listeners) {
+                        listener.onRunningAppsChanged(runningUids);
+                    }
+                });
             }
-        });
+        }
     }
 
     @Override
@@ -354,4 +365,11 @@
         }
         return true;
     }
+
+    @VisibleForTesting
+    int getRunningAppsChangedListenersSizeForTesting() {
+        synchronized (mGenericWindowPolicyControllerLock) {
+            return mRunningAppsChangedListeners.size();
+        }
+    }
 }
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 41044bf..b70cbe3 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -27,7 +27,6 @@
 import java.util.Collection;
 import java.util.List;
 
-
 /**
  * Battery stats local system service interface. This is used to pass internal data out of
  * BatteryStatsImpl, as well as make unchecked calls into BatteryStatsImpl.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c54d7d1..2eaddb1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -470,6 +470,7 @@
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiFunction;
 
 public class ActivityManagerService extends IActivityManager.Stub
@@ -3455,13 +3456,13 @@
     }
 
     /**
-     * @param firstPidOffsets Optional, when it's set, it receives the start/end offset
+     * @param firstPidEndOffset Optional, when it's set, it receives the start/end offset
      *                        of the very first pid to be dumped.
      */
     /* package */ static File dumpStackTraces(ArrayList<Integer> firstPids,
             ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
             ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile,
-            long[] firstPidOffsets, String subject, String criticalEventSection,
+            AtomicLong firstPidEndOffset, String subject, String criticalEventSection,
             AnrLatencyTracker latencyTracker) {
         try {
             if (latencyTracker != null) {
@@ -3534,15 +3535,10 @@
                         + (criticalEventSection != null ? criticalEventSection : ""));
             }
 
-            Pair<Long, Long> offsets = dumpStackTraces(
+            long firstPidEndPos = dumpStackTraces(
                     tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids, latencyTracker);
-            if (firstPidOffsets != null) {
-                if (offsets == null) {
-                    firstPidOffsets[0] = firstPidOffsets[1] = -1;
-                } else {
-                    firstPidOffsets[0] = offsets.first; // Start offset to the ANR trace file
-                    firstPidOffsets[1] = offsets.second; // End offset to the ANR trace file
-                }
+            if (firstPidEndOffset != null) {
+                firstPidEndOffset.set(firstPidEndPos);
             }
 
             return tracesFile;
@@ -3661,9 +3657,9 @@
 
 
     /**
-     * @return The start/end offset of the trace of the very first PID
+     * @return The end offset of the trace of the very first PID
      */
-    public static Pair<Long, Long> dumpStackTraces(String tracesFile,
+    public static long dumpStackTraces(String tracesFile,
             ArrayList<Integer> firstPids, ArrayList<Integer> nativePids,
             ArrayList<Integer> extraPids, AnrLatencyTracker latencyTracker) {
 
@@ -3679,7 +3675,6 @@
         // As applications are usually interested with the ANR stack traces, but we can't share with
         // them the stack traces other than their own stacks. So after the very first PID is
         // dumped, remember the current file size.
-        long firstPidStart = -1;
         long firstPidEnd = -1;
 
         // First collect all of the stacks of the most important pids.
@@ -3692,11 +3687,6 @@
                 final int pid = firstPids.get(i);
                 // We don't copy ANR traces from the system_server intentionally.
                 final boolean firstPid = i == 0 && MY_PID != pid;
-                File tf = null;
-                if (firstPid) {
-                    tf = new File(tracesFile);
-                    firstPidStart = tf.exists() ? tf.length() : 0;
-                }
                 if (latencyTracker != null) {
                     latencyTracker.dumpingPidStarted(pid);
                 }
@@ -3712,11 +3702,11 @@
                 if (remainingTime <= 0) {
                     Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid
                             + "); deadline exceeded.");
-                    return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
+                    return firstPidEnd;
                 }
 
                 if (firstPid) {
-                    firstPidEnd = tf.length();
+                    firstPidEnd = new File(tracesFile).length();
                     // Full latency dump
                     if (latencyTracker != null) {
                         appendtoANRFile(tracesFile,
@@ -3755,7 +3745,7 @@
                 if (remainingTime <= 0) {
                     Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
                         "); deadline exceeded.");
-                    return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
+                    return firstPidEnd;
                 }
 
                 if (DEBUG_ANR) {
@@ -3785,7 +3775,7 @@
                 if (remainingTime <= 0) {
                     Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
                             "); deadline exceeded.");
-                    return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
+                    return firstPidEnd;
                 }
 
                 if (DEBUG_ANR) {
@@ -3800,7 +3790,7 @@
         appendtoANRFile(tracesFile, "----- dumping ended at " + SystemClock.uptimeMillis() + "\n");
         Slog.i(TAG, "Done dumping");
 
-        return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
+        return firstPidEnd;
     }
 
     @Override
@@ -3891,24 +3881,29 @@
                             finishForceStopPackageLocked(packageName, appInfo.uid);
                         }
                     }
-                    final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
-                            Uri.fromParts("package", packageName, null));
-                    intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
-                            | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                    intent.putExtra(Intent.EXTRA_UID, (appInfo != null) ? appInfo.uid : -1);
-                    intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
-                    final int[] visibilityAllowList =
-                            mPackageManagerInt.getVisibilityAllowList(packageName, resolvedUserId);
-                    if (isInstantApp) {
-                        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-                        broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
-                                null, null, null, 0, null, null, permission.ACCESS_INSTANT_APPS,
-                                null, false, false, resolvedUserId, false, null,
-                                visibilityAllowList);
-                    } else {
-                        broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
-                                null, null, null, 0, null, null, null, null, false, false,
-                                resolvedUserId, false, null, visibilityAllowList);
+
+                    if (succeeded) {
+                        final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
+                                Uri.fromParts("package", packageName, null /* fragment */));
+                        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+                                | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                        intent.putExtra(Intent.EXTRA_UID,
+                                (appInfo != null) ? appInfo.uid : INVALID_UID);
+                        intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
+                        if (isInstantApp) {
+                            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+                        }
+                        final int[] visibilityAllowList = mPackageManagerInt.getVisibilityAllowList(
+                                packageName, resolvedUserId);
+
+                        broadcastIntentInPackage("android", null /* featureId */,
+                                SYSTEM_UID, uid, pid, intent, null /* resolvedType */,
+                                null /* resultToApp */, null /* resultTo */, 0 /* resultCode */,
+                                null /* resultData */, null /* resultExtras */,
+                                isInstantApp ? permission.ACCESS_INSTANT_APPS : null,
+                                null /* bOptions */, false /* serialized */, false /* sticky */,
+                                resolvedUserId, false /* allowBackgroundActivityStarts */,
+                                null /* backgroundActivityStartsToken */, visibilityAllowList);
                     }
 
                     if (observer != null) {
@@ -13831,6 +13826,25 @@
         }
     }
 
+    // Apply permission policy around the use of specific broadcast options
+    void enforceBroadcastOptionPermissionsInternal(@Nullable Bundle options, int callingUid) {
+        if (options != null && callingUid != Process.SYSTEM_UID) {
+            if (options.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)) {
+                if (DEBUG_BROADCAST_LIGHT) {
+                    Slog.w(TAG, "Non-system caller " + callingUid
+                            + " may not flag broadcast as alarm");
+                }
+                throw new SecurityException(
+                        "Non-system callers may not flag broadcasts as alarm");
+            }
+            if (options.containsKey(BroadcastOptions.KEY_INTERACTIVE_BROADCAST)) {
+                enforceCallingPermission(
+                        android.Manifest.permission.BROADCAST_OPTION_INTERACTIVE,
+                        "setInteractiveBroadcast");
+            }
+        }
+    }
+
     @GuardedBy("this")
     final int broadcastIntentLocked(ProcessRecord callerApp,
             String callerPackage, String callerFeatureId, Intent intent, String resolvedType,
@@ -13860,6 +13874,9 @@
             @Nullable IBinder backgroundActivityStartsToken,
             @Nullable int[] broadcastAllowList,
             @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
+        // Ensure all internal loopers are registered for idle checks
+        BroadcastLoopers.addMyLooper();
+
         if ((resultTo != null) && (resultToApp == null)) {
             if (resultTo.asBinder() instanceof BinderProxy) {
                 // Warn when requesting results without a way to deliver them
@@ -14695,19 +14712,8 @@
             // We're delivering the result to the caller
             final ProcessRecord resultToApp = callerApp;
 
-            // Non-system callers can't declare that a broadcast is alarm-related.
-            // The PendingIntent invocation case is handled in PendingIntentRecord.
-            if (bOptions != null && callingUid != SYSTEM_UID) {
-                if (bOptions.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)
-                        || bOptions.containsKey(BroadcastOptions.KEY_INTERACTIVE_BROADCAST)) {
-                    if (DEBUG_BROADCAST) {
-                        Slog.w(TAG, "Non-system caller " + callingUid
-                                + " may not flag broadcast as alarm or interactive");
-                    }
-                    throw new SecurityException(
-                            "Non-system callers may not flag broadcasts as alarm or interactive");
-                }
-            }
+            // Permission regimes around sender-supplied broadcast options.
+            enforceBroadcastOptionPermissionsInternal(bOptions, callingUid);
 
             final long origId = Binder.clearCallingIdentity();
             try {
@@ -16763,7 +16769,6 @@
             mAtmInternal.onUserStopped(userId);
             // Clean up various services by removing the user
             mBatteryStatsService.onUserRemoved(userId);
-            mUserController.onUserRemoved(userId);
         }
 
         @Override
@@ -16902,6 +16907,11 @@
             return mEnableModernQueue;
         }
 
+        @Override
+        public void enforceBroadcastOptionsPermissions(Bundle options, int callingUid) {
+            enforceBroadcastOptionPermissionsInternal(options, callingUid);
+        }
+
         /**
          * Returns package name by pid.
          */
@@ -18110,6 +18120,7 @@
 
     public void waitForBroadcastIdle(@Nullable PrintWriter pw) {
         enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()");
+        BroadcastLoopers.waitForIdle(pw);
         for (BroadcastQueue queue : mBroadcastQueues) {
             queue.waitForIdle(pw);
         }
@@ -18121,6 +18132,7 @@
 
     public void waitForBroadcastBarrier(@Nullable PrintWriter pw) {
         enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
+        BroadcastLoopers.waitForIdle(pw);
         for (BroadcastQueue queue : mBroadcastQueues) {
             queue.waitForBarrier(pw);
         }
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 4590c85..417a0e5 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -133,7 +133,7 @@
      */
     public boolean MODERN_QUEUE_ENABLED = DEFAULT_MODERN_QUEUE_ENABLED;
     private static final String KEY_MODERN_QUEUE_ENABLED = "modern_queue_enabled";
-    private static final boolean DEFAULT_MODERN_QUEUE_ENABLED = false;
+    private static final boolean DEFAULT_MODERN_QUEUE_ENABLED = true;
 
     /**
      * For {@link BroadcastQueueModernImpl}: Maximum number of process queues to
diff --git a/services/core/java/com/android/server/am/BroadcastLoopers.java b/services/core/java/com/android/server/am/BroadcastLoopers.java
new file mode 100644
index 0000000..bebb484
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastLoopers.java
@@ -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.server.am;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Collection of {@link Looper} that are known to be used for broadcast dispatch
+ * within the system. This collection can be useful for callers interested in
+ * confirming that all pending broadcasts have been successfully enqueued.
+ */
+public class BroadcastLoopers {
+    private static final String TAG = "BroadcastLoopers";
+
+    private static final ArraySet<Looper> sLoopers = new ArraySet<>();
+
+    /**
+     * Register the given {@link Looper} as possibly having messages that will
+     * dispatch broadcasts.
+     */
+    public static void addLooper(@NonNull Looper looper) {
+        synchronized (sLoopers) {
+            sLoopers.add(Objects.requireNonNull(looper));
+        }
+    }
+
+    /**
+     * If the current thread is hosting a {@link Looper}, then register it as
+     * possibly having messages that will dispatch broadcasts.
+     */
+    public static void addMyLooper() {
+        final Looper looper = Looper.myLooper();
+        if (looper != null) {
+            synchronized (sLoopers) {
+                if (sLoopers.add(looper)) {
+                    Slog.w(TAG, "Found previously unknown looper " + looper.getThread());
+                }
+            }
+        }
+    }
+
+    /**
+     * Wait for all registered {@link Looper} instances to become idle, as
+     * defined by {@link MessageQueue#isIdle()}. Note that {@link Message#when}
+     * still in the future are ignored for the purposes of the idle test.
+     */
+    public static void waitForIdle(@Nullable PrintWriter pw) {
+        final CountDownLatch latch;
+        synchronized (sLoopers) {
+            final int N = sLoopers.size();
+            latch = new CountDownLatch(N);
+            for (int i = 0; i < N; i++) {
+                final MessageQueue queue = sLoopers.valueAt(i).getQueue();
+                if (queue.isIdle()) {
+                    latch.countDown();
+                } else {
+                    queue.addIdleHandler(() -> {
+                        latch.countDown();
+                        return false;
+                    });
+                }
+            }
+        }
+
+        long lastPrint = 0;
+        while (latch.getCount() > 0) {
+            final long now = SystemClock.uptimeMillis();
+            if (now >= lastPrint + 1000) {
+                lastPrint = now;
+                logv("Waiting for " + latch.getCount() + " loopers to drain...", pw);
+            }
+            SystemClock.sleep(100);
+        }
+        logv("Loopers drained!", pw);
+    }
+
+    private static void logv(@NonNull String msg, @Nullable PrintWriter pw) {
+        Slog.v(TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+            pw.flush();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 9ec1fb4..f7d24e9 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -831,7 +831,7 @@
 
     @NeverCompile
     public void dumpLocked(@UptimeMillisLong long now, @NonNull IndentingPrintWriter pw) {
-        if ((mActive == null) && mPending.isEmpty()) return;
+        if ((mActive == null) && isEmpty()) return;
 
         pw.print(toShortString());
         if (isRunnable()) {
@@ -847,6 +847,10 @@
         if (mActive != null) {
             dumpRecord(now, pw, mActive, mActiveIndex, mActiveBlockedUntilTerminalCount);
         }
+        for (SomeArgs args : mPendingUrgent) {
+            final BroadcastRecord r = (BroadcastRecord) args.arg1;
+            dumpRecord(now, pw, r, args.argi1, args.argi2);
+        }
         for (SomeArgs args : mPending) {
             final BroadcastRecord r = (BroadcastRecord) args.arg1;
             dumpRecord(now, pw, r, args.argi1, args.argi2);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index ab19ae3..5750619 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -365,6 +365,13 @@
             BroadcastProcessQueue nextQueue = queue.runnableAtNext;
             final long runnableAt = queue.getRunnableAt();
 
+            // When broadcasts are skipped or failed during list traversal, we
+            // might encounter a queue that is no longer runnable; skip it
+            if (!queue.isRunnable()) {
+                queue = nextQueue;
+                continue;
+            }
+
             // If queues beyond this point aren't ready to run yet, schedule
             // another pass when they'll be runnable
             if (runnableAt > now && !waitingFor) {
@@ -405,21 +412,24 @@
             queue.runningTraceTrackName = TAG + ".mRunning[" + queueIndex + "]";
             queue.runningOomAdjusted = queue.isPendingManifest();
 
+            // If already warm, we can make OOM adjust request immediately;
+            // otherwise we need to wait until process becomes warm
+            if (processWarm) {
+                notifyStartedRunning(queue);
+                updateOomAdj |= queue.runningOomAdjusted;
+            }
+
             // If we're already warm, schedule next pending broadcast now;
             // otherwise we'll wait for the cold start to circle back around
             queue.makeActiveNextPending();
             if (processWarm) {
                 queue.traceProcessRunningBegin();
-                notifyStartedRunning(queue);
                 scheduleReceiverWarmLocked(queue);
             } else {
                 queue.traceProcessStartingBegin();
                 scheduleReceiverColdLocked(queue);
             }
 
-            // Only kick off an OOM adjustment pass if needed
-            updateOomAdj |= queue.runningOomAdjusted;
-
             // Move to considering next runnable queue
             queue = nextQueue;
         }
@@ -457,9 +467,13 @@
             // now; dispatch its next broadcast and clear the slot
             mRunningColdStart = null;
 
+            // Now that we're running warm, we can finally request that OOM
+            // adjust we've been waiting for
+            notifyStartedRunning(queue);
+            mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
+
             queue.traceProcessEnd();
             queue.traceProcessRunningBegin();
-            notifyStartedRunning(queue);
             scheduleReceiverWarmLocked(queue);
 
             // We might be willing to kick off another cold start
@@ -802,9 +816,9 @@
      * ordered broadcast; assumes the sender is still a warm process.
      */
     private void scheduleResultTo(@NonNull BroadcastRecord r) {
-        if ((r.resultToApp == null) || (r.resultTo == null)) return;
+        if (r.resultTo == null) return;
         final ProcessRecord app = r.resultToApp;
-        final IApplicationThread thread = app.getOnewayThread();
+        final IApplicationThread thread = (app != null) ? app.getOnewayThread() : null;
         if (thread != null) {
             mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
                     app, OOM_ADJ_REASON_FINISH_RECEIVER);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 740efbc..3f5f3bc 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityManager.START_SUCCESS;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
@@ -36,7 +35,6 @@
 import android.os.IBinder;
 import android.os.PowerWhitelistManager;
 import android.os.PowerWhitelistManager.ReasonCode;
-import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.TransactionTooLargeException;
@@ -441,17 +439,8 @@
         // Only system senders can declare a broadcast to be alarm-originated.  We check
         // this here rather than in the general case handling below to fail before the other
         // invocation side effects such as allowlisting.
-        if (options != null && callingUid != Process.SYSTEM_UID
-                && key.type == ActivityManager.INTENT_SENDER_BROADCAST) {
-            if (options.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)
-                    || options.containsKey(BroadcastOptions.KEY_INTERACTIVE_BROADCAST)) {
-                if (DEBUG_BROADCAST_LIGHT) {
-                    Slog.w(TAG, "Non-system caller " + callingUid
-                            + " may not flag broadcast as alarm or interactive");
-                }
-                throw new SecurityException(
-                        "Non-system callers may not flag broadcasts as alarm or interactive");
-            }
+        if (key.type == ActivityManager.INTENT_SENDER_BROADCAST) {
+            controller.mAmInternal.enforceBroadcastOptionsPermissions(options, callingUid);
         }
 
         final long origId = Binder.clearCallingIdentity();
@@ -490,6 +479,11 @@
 
             final IApplicationThread finishedReceiverThread = caller;
             boolean sendFinish = finishedReceiver != null;
+            if ((finishedReceiver != null) && (finishedReceiverThread == null)) {
+                Slog.w(TAG, "Sending of " + intent + " from " + Binder.getCallingUid()
+                        + " requested resultTo without an IApplicationThread!", new Throwable());
+            }
+
             int userId = key.userId;
             if (userId == UserHandle.USER_CURRENT) {
                 userId = controller.mUserController.getCurrentOrTargetUserId();
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 71d39964..f461f3d 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -62,6 +62,8 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+
 /**
  * The error state of the process, such as if it's crashing/ANR etc.
  */
@@ -458,10 +460,10 @@
         // avoid spending 1/2 second collecting stats to rank lastPids.
         StringWriter tracesFileException = new StringWriter();
         // To hold the start and end offset to the ANR trace file respectively.
-        final long[] offsets = new long[2];
+        final AtomicLong firstPidEndOffset = new AtomicLong(-1);
         File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
                 isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
-                nativePids, tracesFileException, offsets, annotation, criticalEventLog,
+                nativePids, tracesFileException, firstPidEndOffset, annotation, criticalEventLog,
                 latencyTracker);
 
         if (isMonitorCpuUsage()) {
@@ -478,10 +480,14 @@
         if (tracesFile == null) {
             // There is no trace file, so dump (only) the alleged culprit's threads to the log
             Process.sendSignal(pid, Process.SIGNAL_QUIT);
-        } else if (offsets[1] > 0) {
+        } else if (firstPidEndOffset.get() > 0) {
             // We've dumped into the trace file successfully
+            // We pass the start and end offsets of the first section of
+            // the ANR file (the headers and first process dump)
+            final long startOffset = 0L;
+            final long endOffset = firstPidEndOffset.get();
             mService.mProcessList.mAppExitInfoTracker.scheduleLogAnrTrace(
-                    pid, mApp.uid, mApp.getPackageList(), tracesFile, offsets[0], offsets[1]);
+                    pid, mApp.uid, mApp.getPackageList(), tracesFile, startOffset, endOffset);
         }
 
         // Check if package is still being loaded
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index dcc7a8e..82d239f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -120,6 +120,7 @@
 import com.android.server.SystemServiceManager;
 import com.android.server.am.UserState.KeyEvictedCallback;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
 import com.android.server.pm.UserManagerService;
 import com.android.server.utils.Slogf;
 import com.android.server.utils.TimingsTraceAndSlog;
@@ -320,8 +321,12 @@
     @GuardedBy("mLock")
     private int[] mStartedUserArray = new int[] { 0 };
 
-    // If there are multiple profiles for the current user, their ids are here
-    // Currently only the primary user can have managed profiles
+    /**
+     * Contains the current user and its profiles (if any).
+     *
+     * <p><b>NOTE: </b>it lists all profiles, regardless of their running state (i.e., they're in
+     * this list even if not running).
+     */
     @GuardedBy("mLock")
     private int[] mCurrentProfileIds = new int[] {};
 
@@ -436,6 +441,18 @@
     @GuardedBy("mLock")
     private final SparseBooleanArray mVisibleUsers = new SparseBooleanArray();
 
+    private final UserLifecycleListener mUserLifecycleListener = new UserLifecycleListener() {
+        @Override
+        public void onUserCreated(UserInfo user, Object token) {
+            onUserAdded(user);
+        }
+
+        @Override
+        public void onUserRemoved(UserInfo user) {
+            UserController.this.onUserRemoved(user.id);
+        }
+    };
+
     UserController(ActivityManagerService service) {
         this(new Injector(service));
     }
@@ -1667,7 +1684,7 @@
                     userSwitchUiEnabled = mUserSwitchUiEnabled;
                 }
                 mInjector.updateUserConfiguration();
-                updateCurrentProfileIds();
+                updateProfileRelatedCaches();
                 mInjector.getWindowManager().setCurrentUser(userId);
                 mInjector.reportCurWakefulnessUsageEvent();
                 // Once the internal notion of the active user has switched, we lock the device
@@ -1681,7 +1698,7 @@
                 }
             } else {
                 final Integer currentUserIdInt = mCurrentUserId;
-                updateCurrentProfileIds();
+                updateProfileRelatedCaches();
                 synchronized (mLock) {
                     mUserLru.remove(currentUserIdInt);
                     mUserLru.add(currentUserIdInt);
@@ -2526,7 +2543,8 @@
             Slogf.d(TAG, "onSystemReady()");
 
         }
-        updateCurrentProfileIds();
+        mInjector.getUserManagerInternal().addUserLifecycleListener(mUserLifecycleListener);
+        updateProfileRelatedCaches();
         mInjector.reportCurWakefulnessUsageEvent();
     }
 
@@ -2551,13 +2569,13 @@
     }
 
     /**
-     * Refreshes the list of users related to the current user when either a
-     * user switch happens or when a new related user is started in the
-     * background.
+     * Refreshes the internal caches related to user profiles.
+     *
+     * <p>It's called every time a user is started.
      */
-    private void updateCurrentProfileIds() {
+    private void updateProfileRelatedCaches() {
         final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(getCurrentUserId(),
-                false /* enabledOnly */);
+                /* enabledOnly= */ false);
         int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
         for (int i = 0; i < currentProfileIds.length; i++) {
             currentProfileIds[i] = profiles.get(i).id;
@@ -2824,6 +2842,18 @@
         }
     }
 
+    private void onUserAdded(UserInfo user) {
+        if (!user.isProfile()) {
+            return;
+        }
+        synchronized (mLock) {
+            if (user.profileGroupId == mCurrentUserId) {
+                mCurrentProfileIds = ArrayUtils.appendInt(mCurrentProfileIds, user.id);
+            }
+            mUserProfileGroupIds.put(user.id, user.profileGroupId);
+        }
+    }
+
     void onUserRemoved(@UserIdInt int userId) {
         synchronized (mLock) {
             int size = mUserProfileGroupIds.size();
@@ -2938,6 +2968,10 @@
             for (int i = 0; i < mVisibleUsers.size(); i++) {
                 proto.write(UserControllerProto.VISIBLE_USERS_ARRAY, mVisibleUsers.keyAt(i));
             }
+            proto.write(UserControllerProto.CURRENT_USER, mCurrentUserId);
+            for (int i = 0; i < mCurrentProfileIds.length; i++) {
+                proto.write(UserControllerProto.CURRENT_PROFILES, mCurrentProfileIds[i]);
+            }
             proto.end(token);
         }
     }
@@ -2975,6 +3009,7 @@
                     pw.println(mUserProfileGroupIds.valueAt(i));
                 }
             }
+            pw.println("  mCurrentProfileIds:" + Arrays.toString(mCurrentProfileIds));
             pw.println("  mCurrentUserId:" + mCurrentUserId);
             pw.println("  mTargetUserId:" + mTargetUserId);
             pw.println("  mLastActiveUsers:" + mLastActiveUsers);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index c59ee83..bbffc89 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -344,7 +344,7 @@
         if (AudioService.DEBUG_COMM_RTE) {
             Log.v(TAG, "setCommunicationRouteForClient: device: " + device);
         }
-        AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
+        AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                                         "setCommunicationRouteForClient for pid: " + pid
                                         + " device: " + device
                                         + " from API: " + eventSource)).printLog(TAG));
@@ -1212,7 +1212,7 @@
         if (useCase == AudioSystem.FOR_MEDIA) {
             postReportNewRoutes(fromA2dp);
         }
-        AudioService.sForceUseLogger.log(
+        AudioService.sForceUseLogger.enqueue(
                 new AudioServiceEvents.ForceUseEvent(useCase, config, eventSource));
         new MediaMetrics.Item(MediaMetrics.Name.AUDIO_FORCE_USE + MediaMetrics.SEPARATOR
                 + AudioSystem.forceUseUsageToString(useCase))
@@ -1230,7 +1230,7 @@
     }
 
     private void onSendBecomingNoisyIntent() {
-        AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
+        AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                 "broadcast ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG));
         mSystemServer.sendDeviceBecomingNoisyIntent();
     }
@@ -1468,7 +1468,7 @@
                 case MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT: {
                     final BtDeviceInfo info = (BtDeviceInfo) msg.obj;
                     if (info.mDevice == null) break;
-                    AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
+                    AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                             "msg: onBluetoothActiveDeviceChange "
                                     + " state=" + info.mState
                                     // only querying address as this is the only readily available
@@ -1858,7 +1858,7 @@
             Log.v(TAG, "onUpdateCommunicationRoute, preferredCommunicationDevice: "
                     + preferredCommunicationDevice + " eventSource: " + eventSource);
         }
-        AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
+        AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                 "onUpdateCommunicationRoute, preferredCommunicationDevice: "
                 + preferredCommunicationDevice + " eventSource: " + eventSource)));
 
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index ce36ff8..c8f282f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -309,7 +309,7 @@
             address = "";
         }
 
-        AudioService.sDeviceLogger.log(new EventLogger.StringEvent("BT connected:"
+        AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent("BT connected:"
                         + " addr=" + address
                         + " profile=" + btInfo.mProfile
                         + " state=" + btInfo.mState
@@ -412,13 +412,13 @@
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
             address = "";
         }
-        AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
+        AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                 "onBluetoothA2dpDeviceConfigChange addr=" + address
                     + " event=" + BtHelper.a2dpDeviceEventToString(event)));
 
         synchronized (mDevicesLock) {
             if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
-                AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
+                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                         "A2dp config change ignored (scheduled connection change)")
                         .printLog(TAG));
                 mmi.set(MediaMetrics.Property.EARLY_RETURN, "A2dp config change ignored")
@@ -460,7 +460,7 @@
                     BtHelper.getName(btDevice), a2dpCodec);
 
             if (res != AudioSystem.AUDIO_STATUS_OK) {
-                AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
+                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                         "APM handleDeviceConfigChange failed for A2DP device addr=" + address
                                 + " codec=" + AudioSystem.audioFormatToString(a2dpCodec))
                         .printLog(TAG));
@@ -472,7 +472,7 @@
                                 BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED,
                                 musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
             } else {
-                AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
+                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                         "APM handleDeviceConfigChange success for A2DP device addr=" + address
                                 + " codec=" + AudioSystem.audioFormatToString(a2dpCodec))
                         .printLog(TAG));
@@ -522,7 +522,7 @@
                             AudioDeviceInventory.WiredDeviceConnectionState wdcs) {
         int type = wdcs.mAttributes.getInternalType();
 
-        AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs));
+        AudioService.sDeviceLogger.enqueue(new AudioServiceEvents.WiredDevConnectEvent(wdcs));
 
         MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
                 + "onSetWiredDeviceConnectionState")
@@ -619,7 +619,7 @@
             @NonNull List<AudioDeviceAttributes> devices) {
         final long identity = Binder.clearCallingIdentity();
 
-        AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
+        AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                                 "setPreferredDevicesForStrategySync, strategy: " + strategy
                                 + " devices: " + devices)).printLog(TAG));
         final int status = mAudioSystem.setDevicesRoleForStrategy(
@@ -635,7 +635,7 @@
     /*package*/ int removePreferredDevicesForStrategySync(int strategy) {
         final long identity = Binder.clearCallingIdentity();
 
-        AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
+        AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                 "removePreferredDevicesForStrategySync, strategy: "
                 + strategy)).printLog(TAG));
 
@@ -1000,13 +1000,13 @@
         // TODO: log in MediaMetrics once distinction between connection failure and
         // double connection is made.
         if (res != AudioSystem.AUDIO_STATUS_OK) {
-            AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
+            AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                     "APM failed to make available A2DP device addr=" + address
                             + " error=" + res).printLog(TAG));
             // TODO: connection failed, stop here
             // TODO: return;
         } else {
-            AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
+            AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                     "A2DP device addr=" + address + " now available").printLog(TAG));
         }
 
@@ -1047,7 +1047,7 @@
         if (!deviceToRemoveKey
                 .equals(mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) {
             // removing A2DP device not currently used by AudioPolicy, log but don't act on it
-            AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
+            AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                     "A2DP device " + address + " made unavailable, was not used")).printLog(TAG));
             mmi.set(MediaMetrics.Property.EARLY_RETURN,
                     "A2DP device made unavailable, was not used")
@@ -1062,13 +1062,13 @@
                 AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec);
 
         if (res != AudioSystem.AUDIO_STATUS_OK) {
-            AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
+            AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                     "APM failed to make unavailable A2DP device addr=" + address
                             + " error=" + res).printLog(TAG));
             // TODO:  failed to disconnect, stop here
             // TODO: return;
         } else {
-            AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
+            AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                     "A2DP device addr=" + address + " made unavailable")).printLog(TAG));
         }
         mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
@@ -1314,7 +1314,7 @@
                     && !mDeviceBroker.hasAudioFocusUsers()) {
                 // no media playback, not a "becoming noisy" situation, otherwise it could cause
                 // the pausing of some apps that are playing remotely
-                AudioService.sDeviceLogger.log((new EventLogger.StringEvent(
+                AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                         "dropping ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG));
                 mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return
                 return 0;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f3a9a69..9d6fa9e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -990,7 +990,7 @@
     public AudioService(Context context, AudioSystemAdapter audioSystem,
             SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper,
             AppOpsManager appOps) {
-        sLifecycleLogger.log(new EventLogger.StringEvent("AudioService()"));
+        sLifecycleLogger.enqueue(new EventLogger.StringEvent("AudioService()"));
         mContext = context;
         mContentResolver = context.getContentResolver();
         mAppOps = appOps;
@@ -1539,14 +1539,14 @@
         if (!mSystemReady ||
                 (AudioSystem.checkAudioFlinger() != AudioSystem.AUDIO_STATUS_OK)) {
             Log.e(TAG, "Audioserver died.");
-            sLifecycleLogger.log(new EventLogger.StringEvent(
+            sLifecycleLogger.enqueue(new EventLogger.StringEvent(
                     "onAudioServerDied() audioserver died"));
             sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED, SENDMSG_NOOP, 0, 0,
                     null, 500);
             return;
         }
         Log.i(TAG, "Audioserver started.");
-        sLifecycleLogger.log(new EventLogger.StringEvent(
+        sLifecycleLogger.enqueue(new EventLogger.StringEvent(
                 "onAudioServerDied() audioserver started"));
 
         updateAudioHalPids();
@@ -1776,7 +1776,7 @@
 
         // did it work? check based on status
         if (status != AudioSystem.AUDIO_STATUS_OK) {
-            sLifecycleLogger.log(new EventLogger.StringEvent(
+            sLifecycleLogger.enqueue(new EventLogger.StringEvent(
                     caller + ": initStreamVolume failed with " + status + " will retry")
                     .printLog(ALOGE, TAG));
             sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
@@ -1790,7 +1790,7 @@
         }
 
         // success
-        sLifecycleLogger.log(new EventLogger.StringEvent(
+        sLifecycleLogger.enqueue(new EventLogger.StringEvent(
                 caller + ": initStreamVolume succeeded").printLog(ALOGI, TAG));
     }
 
@@ -1813,7 +1813,7 @@
             }
         }
         if (!success) {
-            sLifecycleLogger.log(new EventLogger.StringEvent(
+            sLifecycleLogger.enqueue(new EventLogger.StringEvent(
                     caller + ": initStreamVolume succeeded but invalid mix/max levels, will retry")
                     .printLog(ALOGW, TAG));
             sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
@@ -2764,7 +2764,7 @@
                 "setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
                 Binder.getCallingUid(), Binder.getCallingPid(), strategy,
                 devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
-        sDeviceLogger.log(new EventLogger.StringEvent(logString).printLog(TAG));
+        sDeviceLogger.enqueue(new EventLogger.StringEvent(logString).printLog(TAG));
         if (devices.stream().anyMatch(device ->
                 device.getRole() == AudioDeviceAttributes.ROLE_INPUT)) {
             Log.e(TAG, "Unsupported input routing in " + logString);
@@ -2784,7 +2784,7 @@
     public int removePreferredDevicesForStrategy(int strategy) {
         final String logString =
                 String.format("removePreferredDeviceForStrategy strat:%d", strategy);
-        sDeviceLogger.log(new EventLogger.StringEvent(logString).printLog(TAG));
+        sDeviceLogger.enqueue(new EventLogger.StringEvent(logString).printLog(TAG));
 
         final int status = mDeviceBroker.removePreferredDevicesForStrategySync(strategy);
         if (status != AudioSystem.SUCCESS) {
@@ -2850,7 +2850,7 @@
                 "setPreferredDevicesForCapturePreset u/pid:%d/%d source:%d dev:%s",
                 Binder.getCallingUid(), Binder.getCallingPid(), capturePreset,
                 devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
-        sDeviceLogger.log(new EventLogger.StringEvent(logString).printLog(TAG));
+        sDeviceLogger.enqueue(new EventLogger.StringEvent(logString).printLog(TAG));
         if (devices.stream().anyMatch(device ->
                 device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT)) {
             Log.e(TAG, "Unsupported output routing in " + logString);
@@ -2871,7 +2871,7 @@
     public int clearPreferredDevicesForCapturePreset(int capturePreset) {
         final String logString = String.format(
                 "removePreferredDeviceForCapturePreset source:%d", capturePreset);
-        sDeviceLogger.log(new EventLogger.StringEvent(logString).printLog(TAG));
+        sDeviceLogger.enqueue(new EventLogger.StringEvent(logString).printLog(TAG));
 
         final int status = mDeviceBroker.clearPreferredDevicesForCapturePresetSync(capturePreset);
         if (status != AudioSystem.SUCCESS) {
@@ -3043,9 +3043,10 @@
                 + ", volControlStream=" + mVolumeControlStream
                 + ", userSelect=" + mUserSelectedVolumeControlStream);
         if (direction != AudioManager.ADJUST_SAME) {
-            sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_SUGG_VOL, suggestedStreamType,
-                    direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
-                    .append("/").append(caller).append(" uid:").append(uid).toString()));
+            sVolumeLogger.enqueue(
+                    new VolumeEvent(VolumeEvent.VOL_ADJUST_SUGG_VOL, suggestedStreamType,
+                            direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
+                            .append("/").append(caller).append(" uid:").append(uid).toString()));
         }
 
         boolean hasExternalVolumeController = notifyExternalVolumeController(direction);
@@ -3144,7 +3145,7 @@
             return;
         }
 
-        sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
+        sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
                 direction/*val1*/, flags/*val2*/, callingPackage));
         adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
                 Binder.getCallingUid(), Binder.getCallingPid(), attributionTag,
@@ -3625,7 +3626,7 @@
         }
         final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
 
-        sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(),
+        sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(),
                 index/*val1*/, flags/*val2*/, callingPackage));
 
         vgs.setVolumeIndex(index, flags);
@@ -3776,7 +3777,7 @@
                 ? new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
                     index/*val1*/, flags/*val2*/, callingPackage)
                 : new DeviceVolumeEvent(streamType, index, device, callingPackage);
-        sVolumeLogger.log(event);
+        sVolumeLogger.enqueue(event);
         setStreamVolume(streamType, index, flags, device,
                 callingPackage, callingPackage, attributionTag,
                 Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
@@ -3977,7 +3978,7 @@
     private void updateHearingAidVolumeOnVoiceActivityUpdate() {
         final int streamType = getBluetoothContextualVolumeStream();
         final int index = getStreamVolume(streamType);
-        sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
+        sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
                 mVoicePlaybackActive.get(), streamType, index));
         mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
 
@@ -4018,7 +4019,7 @@
         if (AudioSystem.isSingleAudioDeviceType(
                 absVolumeMultiModeCaseDevices, AudioSystem.DEVICE_OUT_HEARING_AID)) {
             final int index = getStreamVolume(streamType);
-            sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_MODE_CHANGE_HEARING_AID,
+            sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_MODE_CHANGE_HEARING_AID,
                     newMode, streamType, index));
             mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
         }
@@ -5419,7 +5420,7 @@
                         /*obj*/ null, /*delay*/ 0);
                 int previousMode = mMode.getAndSet(mode);
                 // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
-                mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid,
+                mModeLogger.enqueue(new PhoneStateEvent(requesterPackage, requesterPid,
                         requestedMode, pid, mode));
 
                 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
@@ -5562,7 +5563,7 @@
         }
 
         if (direction != AudioManager.ADJUST_SAME) {
-            sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_VOL_UID, streamType,
+            sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_ADJUST_VOL_UID, streamType,
                     direction/*val1*/, flags/*val2*/,
                     new StringBuilder(packageName).append(" uid:").append(uid)
                     .toString()));
@@ -6892,7 +6893,7 @@
         // verify arguments
         Objects.requireNonNull(device);
         AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
-        sVolumeLogger.log(new EventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
+        sVolumeLogger.enqueue(new EventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
                 + AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
                 + device.getAddress() + " behavior:"
                 + AudioDeviceVolumeManager.volumeBehaviorName(deviceVolumeBehavior)
@@ -6948,7 +6949,7 @@
         }
 
         // log event and caller
-        sDeviceLogger.log(new EventLogger.StringEvent(
+        sDeviceLogger.enqueue(new EventLogger.StringEvent(
                 "Volume behavior " + deviceVolumeBehavior + " for dev=0x"
                       + Integer.toHexString(audioSystemDeviceOut) + " from:" + caller));
         // make sure we have a volume entry for this device, and that volume is updated according
@@ -7594,7 +7595,7 @@
             final int status = AudioSystem.initStreamVolume(
                     streamType, mIndexMin / 10, mIndexMax / 10);
             if (status != AudioSystem.AUDIO_STATUS_OK) {
-                sLifecycleLogger.log(new EventLogger.StringEvent(
+                sLifecycleLogger.enqueue(new EventLogger.StringEvent(
                          "VSS() stream:" + streamType + " initStreamVolume=" + status)
                         .printLog(ALOGE, TAG));
                 sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
@@ -8013,7 +8014,7 @@
                 }
             }
             if (changed) {
-                sVolumeLogger.log(new VolumeEvent(
+                sVolumeLogger.enqueue(new VolumeEvent(
                         VolumeEvent.VOL_MUTE_STREAM_INT, mStreamType, state));
             }
             return changed;
@@ -8183,10 +8184,10 @@
             streamState.setIndex(index, update.mDevice, update.mCaller,
                     // trusted as index is always validated before message is posted
                     true /*hasModifyAudioSettings*/);
-            sVolumeLogger.log(new EventLogger.StringEvent(update.mCaller + " dev:0x"
+            sVolumeLogger.enqueue(new EventLogger.StringEvent(update.mCaller + " dev:0x"
                     + Integer.toHexString(update.mDevice) + " volIdx:" + index));
         } else {
-            sVolumeLogger.log(new EventLogger.StringEvent(update.mCaller
+            sVolumeLogger.enqueue(new EventLogger.StringEvent(update.mCaller
                     + " update vol on dev:0x" + Integer.toHexString(update.mDevice)));
         }
         setDeviceVolume(streamState, update.mDevice);
@@ -8364,7 +8365,7 @@
                             .set(MediaMetrics.Property.FORCE_USE_MODE,
                                     AudioSystem.forceUseConfigToString(config))
                             .record();
-                    sForceUseLogger.log(
+                    sForceUseLogger.enqueue(
                             new AudioServiceEvents.ForceUseEvent(useCase, config, eventSource));
                     mAudioSystem.setForceUse(useCase, config);
                 }
@@ -8633,7 +8634,7 @@
 
     private void avrcpSupportsAbsoluteVolume(String address, boolean support) {
         // address is not used for now, but may be used when multiple a2dp devices are supported
-        sVolumeLogger.log(new EventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
+        sVolumeLogger.enqueue(new EventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
                 + address + " support=" + support).printLog(TAG));
         mDeviceBroker.setAvrcpAbsoluteVolumeSupported(support);
         setAvrcpAbsoluteVolumeSupported(support);
@@ -8767,13 +8768,21 @@
                     UserInfo userInfo = UserManagerService.getInstance().getUserInfo(userId);
                     killBackgroundUserProcessesWithRecordAudioPermission(userInfo);
                 }
-                UserManagerService.getInstance().setUserRestriction(
-                        UserManager.DISALLOW_RECORD_AUDIO, true, userId);
+                try {
+                    UserManagerService.getInstance().setUserRestriction(
+                            UserManager.DISALLOW_RECORD_AUDIO, true, userId);
+                } catch (IllegalArgumentException e) {
+                    Slog.w(TAG, "Failed to apply DISALLOW_RECORD_AUDIO restriction: " + e);
+                }
             } else if (action.equals(Intent.ACTION_USER_FOREGROUND)) {
                 // Enable audio recording for foreground user/profile
                 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-                UserManagerService.getInstance().setUserRestriction(
-                        UserManager.DISALLOW_RECORD_AUDIO, false, userId);
+                try {
+                    UserManagerService.getInstance().setUserRestriction(
+                            UserManager.DISALLOW_RECORD_AUDIO, false, userId);
+                } catch (IllegalArgumentException e) {
+                    Slog.w(TAG, "Failed to apply DISALLOW_RECORD_AUDIO restriction: " + e);
+                }
             } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                 state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
                 if (state == BluetoothAdapter.STATE_OFF ||
@@ -10668,7 +10677,7 @@
                 pcb.asBinder().linkToDeath(app, 0/*flags*/);
 
                 // logging after registration so we have the registration id
-                mDynPolicyLogger.log((new EventLogger.StringEvent("registerAudioPolicy for "
+                mDynPolicyLogger.enqueue((new EventLogger.StringEvent("registerAudioPolicy for "
                         + pcb.asBinder() + " u/pid:" + Binder.getCallingUid() + "/"
                         + Binder.getCallingPid() + " with config:" + app.toCompactLogString()))
                         .printLog(TAG));
@@ -10866,7 +10875,7 @@
 
 
     private void unregisterAudioPolicyInt(@NonNull IAudioPolicyCallback pcb, String operationName) {
-        mDynPolicyLogger.log((new EventLogger.StringEvent(operationName + " for "
+        mDynPolicyLogger.enqueue((new EventLogger.StringEvent(operationName + " for "
                 + pcb.asBinder()).printLog(TAG)));
         synchronized (mAudioPolicies) {
             AudioPolicyProxy app = mAudioPolicies.remove(pcb.asBinder());
@@ -11394,7 +11403,7 @@
         }
 
         public void binderDied() {
-            mDynPolicyLogger.log((new EventLogger.StringEvent("AudioPolicy "
+            mDynPolicyLogger.enqueue((new EventLogger.StringEvent("AudioPolicy "
                     + mPolicyCallback.asBinder() + " died").printLog(TAG)));
             release();
         }
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 399829e..df65dbd 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -263,20 +263,20 @@
     /*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) {
         if (mA2dp == null) {
             if (AudioService.DEBUG_VOL) {
-                AudioService.sVolumeLogger.log(new EventLogger.StringEvent(
+                AudioService.sVolumeLogger.enqueue(new EventLogger.StringEvent(
                         "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp").printLog(TAG));
                 return;
             }
         }
         if (!mAvrcpAbsVolSupported) {
-            AudioService.sVolumeLogger.log(new EventLogger.StringEvent(
+            AudioService.sVolumeLogger.enqueue(new EventLogger.StringEvent(
                     "setAvrcpAbsoluteVolumeIndex: abs vol not supported ").printLog(TAG));
             return;
         }
         if (AudioService.DEBUG_VOL) {
             Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index);
         }
-        AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
+        AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                 AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index));
         mA2dp.setAvrcpAbsoluteVolume(index);
     }
@@ -393,14 +393,14 @@
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
                 @NonNull String eventSource) {
-        AudioService.sDeviceLogger.log(new EventLogger.StringEvent(eventSource));
+        AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource));
         return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
     }
 
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
-        AudioService.sDeviceLogger.log(new EventLogger.StringEvent(eventSource));
+        AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource));
         return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
     }
 
@@ -418,7 +418,7 @@
             Log.i(TAG, "setLeAudioVolume: calling mLeAudio.setVolume idx="
                     + index + " volume=" + volume);
         }
-        AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
+        AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                 AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, index, maxIndex));
         mLeAudio.setVolume(volume);
     }
@@ -443,7 +443,7 @@
         }
         // do not log when hearing aid is not connected to avoid confusion when reading dumpsys
         if (isHeadAidConnected) {
-            AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
+            AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                     AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
         }
         mHearingAid.setVolume(gainDB);
@@ -675,7 +675,7 @@
                         case BluetoothProfile.HEADSET:
                         case BluetoothProfile.HEARING_AID:
                         case BluetoothProfile.LE_AUDIO:
-                            AudioService.sDeviceLogger.log(new EventLogger.StringEvent(
+                            AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                                     "BT profile service: connecting "
                                     + BluetoothProfile.getProfileName(profile) + " profile"));
                             mDeviceBroker.postBtProfileConnected(profile, proxy);
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index e54ee86..5f6f4b1 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -245,7 +245,7 @@
                 return;
             }
             try {
-                PlaybackActivityMonitor.sEventLogger.log(
+                PlaybackActivityMonitor.sEventLogger.enqueue(
                         (new PlaybackActivityMonitor.FadeOutEvent(apc, skipRamp)).printLog(TAG));
                 apc.getPlayerProxy().applyVolumeShaper(
                         FADEOUT_VSHAPE,
@@ -262,7 +262,7 @@
                 final AudioPlaybackConfiguration apc = players.get(piid);
                 if (apc != null) {
                     try {
-                        PlaybackActivityMonitor.sEventLogger.log(
+                        PlaybackActivityMonitor.sEventLogger.enqueue(
                                 (new EventLogger.StringEvent("unfading out piid:"
                                         + piid)).printLog(TAG));
                         apc.getPlayerProxy().applyVolumeShaper(
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 1ca27dd..27687b2 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -185,7 +185,7 @@
                 final FocusRequester focusOwner = stackIterator.next();
                 if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) {
                     clientsToRemove.add(focusOwner.getClientId());
-                    mEventLogger.log((new EventLogger.StringEvent(
+                    mEventLogger.enqueue((new EventLogger.StringEvent(
                             "focus owner:" + focusOwner.getClientId()
                                     + " in uid:" + uid + " pack: " + packageName
                                     + " getting AUDIOFOCUS_LOSS due to app suspension"))
@@ -433,7 +433,7 @@
             FocusRequester fr = stackIterator.next();
             if(fr.hasSameBinder(cb)) {
                 Log.i(TAG, "AudioFocus  removeFocusStackEntryOnDeath(): removing entry for " + cb);
-                mEventLogger.log(new EventLogger.StringEvent(
+                mEventLogger.enqueue(new EventLogger.StringEvent(
                         "focus requester:" + fr.getClientId()
                                 + " in uid:" + fr.getClientUid()
                                 + " pack:" + fr.getPackageName()
@@ -470,7 +470,7 @@
             final FocusRequester fr = owner.getValue();
             if (fr.hasSameBinder(cb)) {
                 ownerIterator.remove();
-                mEventLogger.log(new EventLogger.StringEvent(
+                mEventLogger.enqueue(new EventLogger.StringEvent(
                         "focus requester:" + fr.getClientId()
                                 + " in uid:" + fr.getClientUid()
                                 + " pack:" + fr.getPackageName()
@@ -968,7 +968,7 @@
         // supposed to be alone in bitfield
         final int uid = (flags == AudioManager.AUDIOFOCUS_FLAG_TEST)
                 ? testUid : Binder.getCallingUid();
-        mEventLogger.log((new EventLogger.StringEvent(
+        mEventLogger.enqueue((new EventLogger.StringEvent(
                 "requestAudioFocus() from uid/pid " + uid
                     + "/" + Binder.getCallingPid()
                     + " AA=" + aa.usageToString() + "/" + aa.contentTypeToString()
@@ -1143,7 +1143,7 @@
                 .record();
 
         // AudioAttributes are currently ignored, to be used for zones / a11y
-        mEventLogger.log((new EventLogger.StringEvent(
+        mEventLogger.enqueue((new EventLogger.StringEvent(
                 "abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
                     + "/" + Binder.getCallingPid()
                     + " clientId=" + clientId))
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 1af8c59..74bfa80 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -157,7 +157,7 @@
             if (index >= 0) {
                 if (!disable) {
                     if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
-                        sEventLogger.log(new EventLogger.StringEvent("unbanning uid:" + uid));
+                        sEventLogger.enqueue(new EventLogger.StringEvent("unbanning uid:" + uid));
                     }
                     mBannedUids.remove(index);
                     // nothing else to do, future playback requests from this uid are ok
@@ -168,7 +168,7 @@
                         checkBanPlayer(apc, uid);
                     }
                     if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
-                        sEventLogger.log(new EventLogger.StringEvent("banning uid:" + uid));
+                        sEventLogger.enqueue(new EventLogger.StringEvent("banning uid:" + uid));
                     }
                     mBannedUids.add(new Integer(uid));
                 } // no else to handle, uid already not in list, so enabling again is no-op
@@ -209,7 +209,7 @@
                 updateAllowedCapturePolicy(apc, mAllowedCapturePolicies.get(uid));
             }
         }
-        sEventLogger.log(new NewPlayerEvent(apc));
+        sEventLogger.enqueue(new NewPlayerEvent(apc));
         synchronized(mPlayerLock) {
             mPlayers.put(newPiid, apc);
             maybeMutePlayerAwaitingConnection(apc);
@@ -229,7 +229,7 @@
         synchronized(mPlayerLock) {
             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
             if (checkConfigurationCaller(piid, apc, binderUid)) {
-                sEventLogger.log(new AudioAttrEvent(piid, attr));
+                sEventLogger.enqueue(new AudioAttrEvent(piid, attr));
                 change = apc.handleAudioAttributesEvent(attr);
             } else {
                 Log.e(TAG, "Error updating audio attributes");
@@ -322,7 +322,7 @@
                 return;
             }
 
-            sEventLogger.log(new PlayerEvent(piid, event, eventValue));
+            sEventLogger.enqueue(new PlayerEvent(piid, event, eventValue));
 
             if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_PORT_ID) {
                 mEventHandler.sendMessage(
@@ -332,7 +332,7 @@
                 for (Integer uidInteger: mBannedUids) {
                     if (checkBanPlayer(apc, uidInteger.intValue())) {
                         // player was banned, do not update its state
-                        sEventLogger.log(new EventLogger.StringEvent(
+                        sEventLogger.enqueue(new EventLogger.StringEvent(
                                 "not starting piid:" + piid + " ,is banned"));
                         return;
                     }
@@ -412,7 +412,7 @@
 
     public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid) {
         // no check on UID yet because this is only for logging at the moment
-        sEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid));
+        sEventLogger.enqueue(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid));
     }
 
     public void releasePlayer(int piid, int binderUid) {
@@ -421,7 +421,7 @@
         synchronized(mPlayerLock) {
             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
             if (checkConfigurationCaller(piid, apc, binderUid)) {
-                sEventLogger.log(new EventLogger.StringEvent(
+                sEventLogger.enqueue(new EventLogger.StringEvent(
                         "releasing player piid:" + piid));
                 mPlayers.remove(new Integer(piid));
                 mDuckingManager.removeReleased(apc);
@@ -443,7 +443,7 @@
     }
 
     /*package*/ void onAudioServerDied() {
-        sEventLogger.log(
+        sEventLogger.enqueue(
                 new EventLogger.StringEvent(
                         "clear port id to piid map"));
         synchronized (mPlayerLock) {
@@ -768,7 +768,7 @@
                 }
                 if (mute) {
                     try {
-                        sEventLogger.log((new EventLogger.StringEvent("call: muting piid:"
+                        sEventLogger.enqueue((new EventLogger.StringEvent("call: muting piid:"
                                 + piid + " uid:" + apc.getClientUid())).printLog(TAG));
                         apc.getPlayerProxy().setVolume(0.0f);
                         mMutedPlayers.add(new Integer(piid));
@@ -793,7 +793,7 @@
                 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
                 if (apc != null) {
                     try {
-                        sEventLogger.log(new EventLogger.StringEvent("call: unmuting piid:"
+                        sEventLogger.enqueue(new EventLogger.StringEvent("call: unmuting piid:"
                                 + piid).printLog(TAG));
                         apc.getPlayerProxy().setVolume(1.0f);
                     } catch (Exception e) {
@@ -1081,7 +1081,7 @@
                     return;
                 }
                 try {
-                    sEventLogger.log((new DuckEvent(apc, skipRamp)).printLog(TAG));
+                    sEventLogger.enqueue((new DuckEvent(apc, skipRamp)).printLog(TAG));
                     apc.getPlayerProxy().applyVolumeShaper(
                             DUCK_VSHAPE,
                             skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
@@ -1096,7 +1096,7 @@
                     final AudioPlaybackConfiguration apc = players.get(piid);
                     if (apc != null) {
                         try {
-                            sEventLogger.log((new EventLogger.StringEvent("unducking piid:"
+                            sEventLogger.enqueue((new EventLogger.StringEvent("unducking piid:"
                                     + piid)).printLog(TAG));
                             apc.getPlayerProxy().applyVolumeShaper(
                                     DUCK_ID,
@@ -1310,8 +1310,9 @@
     //==========================================================================================
     void muteAwaitConnection(@NonNull int[] usagesToMute,
             @NonNull AudioDeviceAttributes dev, long timeOutMs) {
-        sEventLogger.loglogi(
-                "muteAwaitConnection() dev:" + dev + " timeOutMs:" + timeOutMs, TAG);
+        sEventLogger.enqueueAndLog(
+                "muteAwaitConnection() dev:" + dev + " timeOutMs:" + timeOutMs,
+                EventLogger.Event.ALOGI, TAG);
         synchronized (mPlayerLock) {
             mutePlayersExpectingDevice(usagesToMute);
             // schedule timeout (remove previously scheduled first)
@@ -1323,7 +1324,8 @@
     }
 
     void cancelMuteAwaitConnection(String source) {
-        sEventLogger.loglogi("cancelMuteAwaitConnection() from:" + source, TAG);
+        sEventLogger.enqueueAndLog("cancelMuteAwaitConnection() from:" + source,
+                EventLogger.Event.ALOGI, TAG);
         synchronized (mPlayerLock) {
             // cancel scheduled timeout, ignore device, only one expected device at a time
             mEventHandler.removeMessages(MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION);
@@ -1346,7 +1348,7 @@
 
     @GuardedBy("mPlayerLock")
     private void mutePlayersExpectingDevice(@NonNull int[] usagesToMute) {
-        sEventLogger.log(new MuteAwaitConnectionEvent(usagesToMute));
+        sEventLogger.enqueue(new MuteAwaitConnectionEvent(usagesToMute));
         mMutedUsagesAwaitingConnection = usagesToMute;
         final Set<Integer> piidSet = mPlayers.keySet();
         final Iterator<Integer> piidIterator = piidSet.iterator();
@@ -1369,7 +1371,7 @@
         for (int usage : mMutedUsagesAwaitingConnection) {
             if (usage == apc.getAudioAttributes().getUsage()) {
                 try {
-                    sEventLogger.log((new EventLogger.StringEvent(
+                    sEventLogger.enqueue((new EventLogger.StringEvent(
                             "awaiting connection: muting piid:"
                                     + apc.getPlayerInterfaceId()
                                     + " uid:" + apc.getClientUid())).printLog(TAG));
@@ -1394,7 +1396,7 @@
                 continue;
             }
             try {
-                sEventLogger.log(new EventLogger.StringEvent(
+                sEventLogger.enqueue(new EventLogger.StringEvent(
                         "unmuting piid:" + piid).printLog(TAG));
                 apc.getPlayerProxy().applyVolumeShaper(MUTE_AWAIT_CONNECTION_VSHAPE,
                         VolumeShaper.Operation.REVERSE);
@@ -1452,8 +1454,9 @@
             public void handleMessage(Message msg) {
                 switch (msg.what) {
                     case MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION:
-                        sEventLogger.loglogi("Timeout for muting waiting for "
-                                + (AudioDeviceAttributes) msg.obj + ", unmuting", TAG);
+                        sEventLogger.enqueueAndLog("Timeout for muting waiting for "
+                                + (AudioDeviceAttributes) msg.obj + ", unmuting",
+                                EventLogger.Event.ALOGI, TAG);
                         synchronized (mPlayerLock) {
                             unmutePlayersExpectingDevice();
                         }
@@ -1476,7 +1479,7 @@
                         synchronized (mPlayerLock) {
                             int piid = msg.arg1;
 
-                            sEventLogger.log(
+                            sEventLogger.enqueue(
                                     new PlayerEvent(piid, PLAYER_UPDATE_MUTED, eventValue));
 
                             final AudioPlaybackConfiguration apc = mPlayers.get(piid);
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 2ba8882..652ea52 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -164,7 +164,7 @@
         }
         if (MediaRecorder.isSystemOnlyAudioSource(source)) {
             // still want to log event, it just won't appear in recording configurations;
-            sEventLogger.log(new RecordingEvent(event, riid, config).printLog(TAG));
+            sEventLogger.enqueue(new RecordingEvent(event, riid, config).printLog(TAG));
             return;
         }
         dispatchCallbacks(updateSnapshot(event, riid, config));
@@ -204,7 +204,7 @@
                 ? AudioManager.RECORD_CONFIG_EVENT_STOP : AudioManager.RECORD_CONFIG_EVENT_NONE;
         if (riid == AudioManager.RECORD_RIID_INVALID
                 || configEvent == AudioManager.RECORD_CONFIG_EVENT_NONE) {
-            sEventLogger.log(new RecordingEvent(event, riid, null).printLog(TAG));
+            sEventLogger.enqueue(new RecordingEvent(event, riid, null).printLog(TAG));
             return;
         }
         dispatchCallbacks(updateSnapshot(configEvent, riid, null));
@@ -301,7 +301,7 @@
                 if (!state.hasDeathHandler()) {
                     if (state.isActiveConfiguration()) {
                         configChanged = true;
-                        sEventLogger.log(new RecordingEvent(
+                        sEventLogger.enqueue(new RecordingEvent(
                                         AudioManager.RECORD_CONFIG_EVENT_RELEASE,
                                         state.getRiid(), state.getConfig()));
                     }
@@ -486,7 +486,7 @@
                     configChanged = false;
             }
             if (configChanged) {
-                sEventLogger.log(new RecordingEvent(event, riid, state.getConfig()));
+                sEventLogger.enqueue(new RecordingEvent(event, riid, state.getConfig()));
                 configs = getActiveRecordingConfigurations(true /*isPrivileged*/);
             }
         }
diff --git a/services/core/java/com/android/server/audio/SoundEffectsHelper.java b/services/core/java/com/android/server/audio/SoundEffectsHelper.java
index 93eba50..79b54eb 100644
--- a/services/core/java/com/android/server/audio/SoundEffectsHelper.java
+++ b/services/core/java/com/android/server/audio/SoundEffectsHelper.java
@@ -164,7 +164,7 @@
     }
 
     private void logEvent(String msg) {
-        mSfxLogger.log(new EventLogger.StringEvent(msg));
+        mSfxLogger.enqueue(new EventLogger.StringEvent(msg));
     }
 
     // All the methods below run on the worker thread
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 1563d33..2b525f1 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -1708,11 +1708,11 @@
 
 
     private static void loglogi(String msg) {
-        AudioService.sSpatialLogger.loglogi(msg, TAG);
+        AudioService.sSpatialLogger.enqueueAndLog(msg, EventLogger.Event.ALOGI, TAG);
     }
 
     private static String logloge(String msg) {
-        AudioService.sSpatialLogger.loglog(msg, EventLogger.Event.ALOGE, TAG);
+        AudioService.sSpatialLogger.enqueueAndLog(msg, EventLogger.Event.ALOGE, TAG);
         return msg;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 1d90954..055c63d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -38,8 +38,7 @@
  * Abstract {@link HalClientMonitor} subclass that operations eligible/interested in acquisition
  * messages should extend.
  */
-public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implements Interruptable,
-        ErrorConsumer {
+public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implements ErrorConsumer {
 
     private static final String TAG = "Biometrics/AcquisitionClient";
 
@@ -217,4 +216,9 @@
                     HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
         }
     }
+
+    @Override
+    public boolean isInterruptable() {
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index da7781a..0216e49 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -193,9 +193,9 @@
         }
 
         // If the current client dies we should cancel the current operation.
-        if (this instanceof Interruptable) {
+        if (this.isInterruptable()) {
             Slog.e(TAG, "Binder died, cancelling client");
-            ((Interruptable) this).cancel();
+            this.cancel();
         }
         mToken = null;
         if (clearListener) {
@@ -320,4 +320,12 @@
         }
         callback.onClientFinished(this, true /* success */);
     }
+
+    /**
+     * Checks if other client monitor can interrupt current client monitor
+     * @return if current client can be interrupted
+     */
+    public boolean isInterruptable() {
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
index dacec38..4825f1d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -46,7 +46,7 @@
 
     /**
      * The operation is added to the list of pending operations, but a subsequent operation
-     * has been added. This state only applies to {@link Interruptable} operations. When this
+     * has been added. This state only applies to interruptable operations. When this
      * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
      */
     protected static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
@@ -347,9 +347,9 @@
         return mClientMonitor == clientMonitor;
     }
 
-    /** If this operation is {@link Interruptable}. */
+    /** If this operation is interruptable. */
     public boolean isInterruptable() {
-        return mClientMonitor instanceof Interruptable;
+        return mClientMonitor.isInterruptable();
     }
 
     private boolean isHalOperation() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
deleted file mode 100644
index 4f645ef..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.biometrics.sensors;
-
-import android.annotation.NonNull;
-
-/**
- * Interface that {@link BaseClientMonitor} subclasses eligible for cancellation should implement.
- */
-public interface Interruptable {
-    /**
-     * Requests to end the ClientMonitor's lifecycle.
-     */
-    void cancel();
-
-    /**
-     * Notifies the client that it needs to finish before
-     * {@link BaseClientMonitor#start(ClientMonitorCallback)} was invoked. This usually happens
-     * if the client is still waiting in the pending queue and got notified that a subsequent
-     * operation is preempting it.
-     *
-     * This method must invoke
-     * {@link ClientMonitorCallback#onClientFinished(BaseClientMonitor, boolean)} on the
-     * given callback (with success).
-     *
-     * @param callback invoked when the operation is completed.
-     */
-    void cancelWithoutStarting(@NonNull ClientMonitorCallback callback);
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
index aeb6b6e..969a174 100644
--- a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
+++ b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.hardware.biometrics.BiometricOverlayConstants;
 import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
 import android.os.RemoteException;
@@ -43,6 +44,7 @@
 
     @NonNull private final Optional<IUdfpsOverlayController> mUdfpsOverlayController;
     @NonNull private final Optional<ISidefpsController> mSidefpsController;
+    @NonNull private final Optional<IUdfpsOverlay> mUdfpsOverlay;
 
     /**
      * Create an overlay controller for each modality.
@@ -52,9 +54,11 @@
      */
     public SensorOverlays(
             @Nullable IUdfpsOverlayController udfpsOverlayController,
-            @Nullable ISidefpsController sidefpsController) {
+            @Nullable ISidefpsController sidefpsController,
+            @Nullable IUdfpsOverlay udfpsOverlay) {
         mUdfpsOverlayController = Optional.ofNullable(udfpsOverlayController);
         mSidefpsController = Optional.ofNullable(sidefpsController);
+        mUdfpsOverlay = Optional.ofNullable(udfpsOverlay);
     }
 
     /**
@@ -90,6 +94,14 @@
                 Slog.e(TAG, "Remote exception when showing the UDFPS overlay", e);
             }
         }
+
+        if (mUdfpsOverlay.isPresent()) {
+            try {
+                mUdfpsOverlay.get().show(client.getRequestId(), sensorId, reason);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception when showing the new UDFPS overlay", e);
+            }
+        }
     }
 
     /**
@@ -113,6 +125,14 @@
                 Slog.e(TAG, "Remote exception when hiding the UDFPS overlay", e);
             }
         }
+
+        if (mUdfpsOverlay.isPresent()) {
+            try {
+                mUdfpsOverlay.get().hide(sensorId);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception when hiding the new udfps overlay", e);
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 800d4b8..0f5cdc3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -59,7 +59,6 @@
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.EnumerateConsumer;
 import com.android.server.biometrics.sensors.ErrorConsumer;
-import com.android.server.biometrics.sensors.Interruptable;
 import com.android.server.biometrics.sensors.LockoutCache;
 import com.android.server.biometrics.sensors.LockoutConsumer;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -638,7 +637,7 @@
 
     public void onBinderDied() {
         final BaseClientMonitor client = mScheduler.getCurrentClient();
-        if (client instanceof Interruptable) {
+        if (client.isInterruptable()) {
             Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
             final ErrorConsumer errorConsumer = (ErrorConsumer) client;
             errorConsumer.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index b0dc28d..156e6bb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -52,6 +52,7 @@
 import android.hardware.fingerprint.IFingerprintService;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Binder;
 import android.os.Build;
@@ -874,6 +875,14 @@
 
         @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override
+        public void setUdfpsOverlay(@NonNull IUdfpsOverlay controller) {
+            for (ServiceProvider provider : mRegistry.getProviders()) {
+                provider.setUdfpsOverlay(controller);
+            }
+        }
+
+        @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+        @Override
         public void onPowerPressed() {
             for (ServiceProvider provider : mRegistry.getProviders()) {
                 provider.onPowerPressed();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 0c29f56..05c2e29 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -26,6 +26,7 @@
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 
@@ -129,6 +130,12 @@
 
     void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
 
+    /**
+     * Sets udfps overlay
+     * @param controller udfps overlay
+     */
+    void setUdfpsOverlay(@NonNull IUdfpsOverlay controller);
+
     void onPowerPressed();
 
     /**
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 2e5663d..7f1fb1c 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
@@ -33,6 +33,7 @@
 import android.hardware.biometrics.fingerprint.PointerContext;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Build;
 import android.os.Handler;
@@ -118,6 +119,7 @@
             @NonNull LockoutCache lockoutCache,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
             @Nullable ISidefpsController sidefpsController,
+            @Nullable IUdfpsOverlay udfpsOverlay,
             boolean allowBackgroundAuthentication,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @NonNull Handler handler,
@@ -145,7 +147,8 @@
                 false /* isKeyguardBypassEnabled */);
         setRequestId(requestId);
         mLockoutCache = lockoutCache;
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        mSensorOverlays = new SensorOverlays(udfpsOverlayController,
+                sidefpsController, udfpsOverlay);
         mSensorProps = sensorProps;
         mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
         mHandler = handler;
@@ -248,8 +251,8 @@
                     if (authenticated && mSensorProps.isAnySidefpsType()) {
                         if (mHandler.hasMessages(MESSAGE_IGNORE_AUTH)) {
                             Slog.i(TAG, "(sideFPS) Ignoring auth due to recent power press");
-                            onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0,
-                                    true);
+                            onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED,
+                                    0, true);
                             return;
                         }
                         delay = isKeyguard() ? mWaitForAuthKeyguard : mWaitForAuthBp;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 0e89814..5282234 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricOverlayConstants;
 import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -54,12 +55,15 @@
             @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull String owner, int sensorId,
             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
-            @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric) {
+            @Nullable IUdfpsOverlayController udfpsOverlayController,
+            @Nullable IUdfpsOverlay udfpsOverlay,
+            boolean isStrongBiometric) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 true /* shouldVibrate */, biometricLogger, biometricContext);
         setRequestId(requestId);
         mIsStrongBiometric = isStrongBiometric;
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController*/);
+        mSensorOverlays = new SensorOverlays(udfpsOverlayController,
+                null /* sideFpsController*/, udfpsOverlay);
     }
 
     @Override
@@ -82,7 +86,8 @@
 
     @Override
     protected void startHalOperation() {
-        mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD, this);
+        mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
+                this);
 
         try {
             mCancellationSignal = doDetectInteraction();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 612d906..7e5d39f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -30,6 +30,7 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.os.IBinder;
@@ -86,6 +87,7 @@
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
             @Nullable ISidefpsController sidefpsController,
+            @Nullable IUdfpsOverlay udfpsOverlay,
             int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
         // UDFPS haptics occur when an image is acquired (instead of when the result is known)
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
@@ -93,7 +95,8 @@
                 biometricContext);
         setRequestId(requestId);
         mSensorProps = sensorProps;
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        mSensorOverlays = new SensorOverlays(udfpsOverlayController,
+                sidefpsController, udfpsOverlay);
         mMaxTemplatesPerUser = maxTemplatesPerUser;
 
         mALSProbeCallback = getLogger().getAmbientLightProbe(true /* startWithClient */);
@@ -162,7 +165,8 @@
 
     @Override
     protected void startHalOperation() {
-        mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), this);
+        mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason),
+                this);
 
         BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
         try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 628c16a..a42ff9a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -40,6 +40,7 @@
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Binder;
 import android.os.Handler;
@@ -108,6 +109,7 @@
     @Nullable private IFingerprint mDaemon;
     @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
     @Nullable private ISidefpsController mSidefpsController;
+    @Nullable private IUdfpsOverlay mUdfpsOverlay;
 
     private final class BiometricTaskStackListener extends TaskStackListener {
         @Override
@@ -383,7 +385,8 @@
                             BiometricsProtoEnums.CLIENT_UNKNOWN),
                     mBiometricContext,
                     mSensors.get(sensorId).getSensorProperties(),
-                    mUdfpsOverlayController, mSidefpsController, maxTemplatesPerUser, enrollReason);
+                    mUdfpsOverlayController, mSidefpsController, mUdfpsOverlay,
+                    maxTemplatesPerUser, enrollReason);
             scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
                     mBiometricStateCallback, new ClientMonitorCallback() {
                 @Override
@@ -418,7 +421,7 @@
                     opPackageName, sensorId,
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
                     mBiometricContext,
-                    mUdfpsOverlayController, isStrongBiometric);
+                    mUdfpsOverlayController, mUdfpsOverlay, isStrongBiometric);
             scheduleForSensor(sensorId, client, mBiometricStateCallback);
         });
 
@@ -439,10 +442,10 @@
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
                     mBiometricContext, isStrongBiometric,
                     mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
-                    mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication,
+                    mUdfpsOverlayController, mSidefpsController, mUdfpsOverlay,
+                    allowBackgroundAuthentication,
                     mSensors.get(sensorId).getSensorProperties(), mHandler,
-                    Utils.getCurrentStrength(sensorId),
-                    SystemClock.elapsedRealtimeClock());
+                    Utils.getCurrentStrength(sensorId), SystemClock.elapsedRealtimeClock());
             scheduleForSensor(sensorId, client, mBiometricStateCallback);
         });
     }
@@ -649,6 +652,11 @@
     }
 
     @Override
+    public void setUdfpsOverlay(@NonNull IUdfpsOverlay controller) {
+        mUdfpsOverlay = controller;
+    }
+
+    @Override
     public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
             boolean clearSchedulerBuffer) {
         if (mSensors.contains(sensorId)) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 0e6df8e..dbc96df 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -38,6 +38,7 @@
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Handler;
 import android.os.IBinder;
@@ -120,6 +121,7 @@
     @NonNull private final HalResultController mHalResultController;
     @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
     @Nullable private ISidefpsController mSidefpsController;
+    @Nullable private IUdfpsOverlay mUdfpsOverlay;
     @NonNull private final BiometricContext mBiometricContext;
     // for requests that do not use biometric prompt
     @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
@@ -594,7 +596,7 @@
                     createLogger(BiometricsProtoEnums.ACTION_ENROLL,
                             BiometricsProtoEnums.CLIENT_UNKNOWN),
                     mBiometricContext,
-                    mUdfpsOverlayController, mSidefpsController,
+                    mUdfpsOverlayController, mSidefpsController, mUdfpsOverlay,
                     enrollReason);
             mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
@@ -640,7 +642,7 @@
                     mLazyDaemon, token, id, listener, userId, opPackageName,
                     mSensorProperties.sensorId,
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
-                    mBiometricContext, mUdfpsOverlayController,
+                    mBiometricContext, mUdfpsOverlayController, mUdfpsOverlay,
                     isStrongBiometric);
             mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
         });
@@ -664,7 +666,7 @@
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
                     mBiometricContext, isStrongBiometric,
                     mTaskStackListener, mLockoutTracker,
-                    mUdfpsOverlayController, mSidefpsController,
+                    mUdfpsOverlayController, mSidefpsController, mUdfpsOverlay,
                     allowBackgroundAuthentication, mSensorProperties);
             mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
         });
@@ -853,6 +855,11 @@
     }
 
     @Override
+    public void setUdfpsOverlay(@NonNull IUdfpsOverlay controller) {
+        mUdfpsOverlay = controller;
+    }
+
+    @Override
     public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
             boolean clearSchedulerBuffer) {
         final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 0d620fd..56fa36e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -26,6 +26,7 @@
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -76,15 +77,18 @@
             @NonNull LockoutFrameworkImpl lockoutTracker,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
             @Nullable ISidefpsController sidefpsController,
+            @Nullable IUdfpsOverlay udfpsOverlay,
             boolean allowBackgroundAuthentication,
             @NonNull FingerprintSensorPropertiesInternal sensorProps) {
         super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
                 owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
-                isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication,
-                false /* shouldVibrate */, false /* isKeyguardBypassEnabled */);
+                isStrongBiometric, taskStackListener, lockoutTracker,
+                allowBackgroundAuthentication, false /* shouldVibrate */,
+                false /* isKeyguardBypassEnabled */);
         setRequestId(requestId);
         mLockoutFrameworkImpl = lockoutTracker;
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        mSensorOverlays = new SensorOverlays(udfpsOverlayController,
+                sidefpsController, udfpsOverlay);
         mSensorProps = sensorProps;
         mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index c2929d0..3e9b8ef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -23,6 +23,7 @@
 import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricOverlayConstants;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -62,11 +63,13 @@
             @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
             int sensorId,
             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
-            @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric) {
+            @Nullable IUdfpsOverlayController udfpsOverlayController,
+            @Nullable IUdfpsOverlay udfpsOverlay, boolean isStrongBiometric) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 true /* shouldVibrate */, biometricLogger, biometricContext);
         setRequestId(requestId);
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
+        mSensorOverlays = new SensorOverlays(udfpsOverlayController,
+                null /* sideFpsController */, udfpsOverlay);
         mIsStrongBiometric = isStrongBiometric;
     }
 
@@ -92,7 +95,8 @@
 
     @Override
     protected void startHalOperation() {
-        mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD, this);
+        mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
+                this);
 
         try {
             getFreshDaemon().authenticate(0 /* operationId */, getTargetUserId());
@@ -128,8 +132,8 @@
     }
 
     @Override
-    public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated,
-            ArrayList<Byte> hardwareAuthToken) {
+    public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
         getLogger().logOnAuthenticated(getContext(), getOperationContext(),
                 authenticated, false /* requireConfirmation */,
                 getTargetUserId(), false /* isBiometricPrompt */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 5d9af53..3371cec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -26,6 +26,7 @@
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -67,12 +68,14 @@
             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
             @Nullable ISidefpsController sidefpsController,
+            @Nullable IUdfpsOverlay udfpsOverlay,
             @FingerprintManager.EnrollReason int enrollReason) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, sensorId, true /* shouldVibrate */, biometricLogger,
                 biometricContext);
         setRequestId(requestId);
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
+        mSensorOverlays = new SensorOverlays(udfpsOverlayController,
+                sidefpsController, udfpsOverlay);
 
         mEnrollReason = enrollReason;
         if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
@@ -102,7 +105,8 @@
 
     @Override
     protected void startHalOperation() {
-        mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), this);
+        mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason),
+                this);
 
         BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
         try {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6795b6b..c671a2c 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -28,6 +28,7 @@
 import static android.os.PowerWhitelistManager.REASON_VPN;
 import static android.os.UserHandle.PER_USER_RANGE;
 
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
 import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
 
 import static java.util.Objects.requireNonNull;
@@ -79,10 +80,12 @@
 import android.net.RouteInfo;
 import android.net.UidRangeParcel;
 import android.net.UnderlyingNetworkInfo;
+import android.net.Uri;
 import android.net.VpnManager;
 import android.net.VpnProfileState;
 import android.net.VpnService;
 import android.net.VpnTransportInfo;
+import android.net.ipsec.ike.ChildSaProposal;
 import android.net.ipsec.ike.ChildSessionCallback;
 import android.net.ipsec.ike.ChildSessionConfiguration;
 import android.net.ipsec.ike.ChildSessionParams;
@@ -92,6 +95,7 @@
 import android.net.ipsec.ike.IkeSessionConnectionInfo;
 import android.net.ipsec.ike.IkeSessionParams;
 import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.ipsec.ike.exceptions.IkeIOException;
 import android.net.ipsec.ike.exceptions.IkeNetworkLostException;
 import android.net.ipsec.ike.exceptions.IkeNonProtocolException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
@@ -139,6 +143,7 @@
 import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.net.BaseNetworkObserver;
+import com.android.server.vcn.util.MtuUtils;
 import com.android.server.vcn.util.PersistableBundleUtils;
 
 import libcore.io.IoUtils;
@@ -151,6 +156,8 @@
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
@@ -164,6 +171,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -226,6 +234,16 @@
     private static final int VPN_DEFAULT_SCORE = 101;
 
     /**
+     * The reset session timer for data stall. If a session has not successfully revalidated after
+     * the delay, the session will be torn down and restarted in an attempt to recover. Delay
+     * counter is reset on successful validation only.
+     *
+     * <p>If retries have exceeded the length of this array, the last entry in the array will be
+     * used as a repeating interval.
+     */
+    private static final long[] DATA_STALL_RESET_DELAYS_SEC = {30L, 60L, 120L, 240L, 480L, 960L};
+
+    /**
      * The initial token value of IKE session.
      */
     private static final int STARTING_TOKEN = -1;
@@ -271,6 +289,7 @@
     private final UserManager mUserManager;
 
     private final VpnProfileStore mVpnProfileStore;
+    protected boolean mDataStallSuspected = false;
 
     @VisibleForTesting
     VpnProfileStore getVpnProfileStore() {
@@ -522,10 +541,46 @@
                 @NonNull LinkProperties lp,
                 @NonNull NetworkScore score,
                 @NonNull NetworkAgentConfig config,
-                @Nullable NetworkProvider provider) {
+                @Nullable NetworkProvider provider,
+                @Nullable ValidationStatusCallback callback) {
             return new VpnNetworkAgentWrapper(
-                    context, looper, logTag, nc, lp, score, config, provider);
+                    context, looper, logTag, nc, lp, score, config, provider, callback);
         }
+
+        /**
+         * Get the length of time to wait before resetting the ike session when a data stall is
+         * suspected.
+         */
+        public long getDataStallResetSessionSeconds(int count) {
+            if (count >= DATA_STALL_RESET_DELAYS_SEC.length) {
+                return DATA_STALL_RESET_DELAYS_SEC[DATA_STALL_RESET_DELAYS_SEC.length - 1];
+            } else {
+                return DATA_STALL_RESET_DELAYS_SEC[count];
+            }
+        }
+
+        /** Gets the MTU of an interface using Java NetworkInterface primitives */
+        public int getJavaNetworkInterfaceMtu(@Nullable String iface, int defaultValue)
+                throws SocketException {
+            if (iface == null) return defaultValue;
+
+            final NetworkInterface networkInterface = NetworkInterface.getByName(iface);
+            return networkInterface == null ? defaultValue : networkInterface.getMTU();
+        }
+
+        /** Calculates the VPN Network's max MTU based on underlying network and configuration */
+        public int calculateVpnMtu(
+                @NonNull List<ChildSaProposal> childProposals,
+                int maxMtu,
+                int underlyingMtu,
+                boolean isIpv4) {
+            return MtuUtils.getMtu(childProposals, maxMtu, underlyingMtu, isIpv4);
+        }
+    }
+
+    @VisibleForTesting
+    interface ValidationStatusCallback {
+        void onValidationStatus(int status);
     }
 
     public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
@@ -1367,6 +1422,11 @@
     }
 
     private LinkProperties makeLinkProperties() {
+        // The design of disabling IPv6 is only enabled for IKEv2 VPN because it needs additional
+        // logic to handle IPv6 only VPN, and the IPv6 only VPN may be restarted when its MTU
+        // is lower than 1280. The logic is controlled by IKEv2VpnRunner, so the design is only
+        // enabled for IKEv2 VPN.
+        final boolean disableIPV6 = (isIkev2VpnRunner() && mConfig.mtu < IPV6_MIN_MTU);
         boolean allowIPv4 = mConfig.allowIPv4;
         boolean allowIPv6 = mConfig.allowIPv6;
 
@@ -1376,6 +1436,7 @@
 
         if (mConfig.addresses != null) {
             for (LinkAddress address : mConfig.addresses) {
+                if (disableIPV6 && address.isIpv6()) continue;
                 lp.addLinkAddress(address);
                 allowIPv4 |= address.getAddress() instanceof Inet4Address;
                 allowIPv6 |= address.getAddress() instanceof Inet6Address;
@@ -1384,8 +1445,9 @@
 
         if (mConfig.routes != null) {
             for (RouteInfo route : mConfig.routes) {
+                final InetAddress address = route.getDestination().getAddress();
+                if (disableIPV6 && address instanceof Inet6Address) continue;
                 lp.addRoute(route);
-                InetAddress address = route.getDestination().getAddress();
 
                 if (route.getType() == RouteInfo.RTN_UNICAST) {
                     allowIPv4 |= address instanceof Inet4Address;
@@ -1396,7 +1458,8 @@
 
         if (mConfig.dnsServers != null) {
             for (String dnsServer : mConfig.dnsServers) {
-                InetAddress address = InetAddresses.parseNumericAddress(dnsServer);
+                final InetAddress address = InetAddresses.parseNumericAddress(dnsServer);
+                if (disableIPV6 && address instanceof Inet6Address) continue;
                 lp.addDnsServer(address);
                 allowIPv4 |= address instanceof Inet4Address;
                 allowIPv6 |= address instanceof Inet6Address;
@@ -1410,7 +1473,7 @@
                     NetworkStackConstants.IPV4_ADDR_ANY, 0), null /*gateway*/,
                     null /*iface*/, RTN_UNREACHABLE));
         }
-        if (!allowIPv6) {
+        if (!allowIPv6 || disableIPV6) {
             lp.addRoute(new RouteInfo(new IpPrefix(
                     NetworkStackConstants.IPV6_ADDR_ANY, 0), null /*gateway*/,
                     null /*iface*/, RTN_UNREACHABLE));
@@ -1460,6 +1523,11 @@
 
     @GuardedBy("this")
     private void agentConnect() {
+        agentConnect(null /* validationCallback */);
+    }
+
+    @GuardedBy("this")
+    private void agentConnect(@Nullable ValidationStatusCallback validationCallback) {
         LinkProperties lp = makeLinkProperties();
 
         // VPN either provide a default route (IPv4 or IPv6 or both), or they are a split tunnel
@@ -1507,7 +1575,7 @@
         mNetworkAgent = mDeps.newNetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
                 mNetworkCapabilities, lp,
                 new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(),
-                networkAgentConfig, mNetworkProvider);
+                networkAgentConfig, mNetworkProvider, validationCallback);
         final long token = Binder.clearCallingIdentity();
         try {
             mNetworkAgent.register();
@@ -1541,6 +1609,18 @@
         updateState(DetailedState.DISCONNECTED, "agentDisconnect");
     }
 
+    @GuardedBy("this")
+    private void startNewNetworkAgent(NetworkAgent oldNetworkAgent, String reason) {
+        // Initialize the state for a new agent, while keeping the old one connected
+        // in case this new connection fails.
+        mNetworkAgent = null;
+        updateState(DetailedState.CONNECTING, reason);
+        // Bringing up a new NetworkAgent to prevent the data leakage before tearing down the old
+        // NetworkAgent.
+        agentConnect();
+        agentDisconnect(oldNetworkAgent);
+    }
+
     /**
      * Establish a VPN network and return the file descriptor of the VPN interface. This methods
      * returns {@code null} if the application is revoked or not prepared.
@@ -1630,16 +1710,7 @@
                     setUnderlyingNetworks(config.underlyingNetworks);
                 }
             } else {
-                // Initialize the state for a new agent, while keeping the old one connected
-                // in case this new connection fails.
-                mNetworkAgent = null;
-                updateState(DetailedState.CONNECTING, "establish");
-                // Set up forwarding and DNS rules.
-                agentConnect();
-                // Remove the old tun's user forwarding rules
-                // The new tun's user rules have already been added above so they will take over
-                // as rules are deleted. This prevents data leakage as the rules are moved over.
-                agentDisconnect(oldNetworkAgent);
+                startNewNetworkAgent(oldNetworkAgent, "establish");
             }
 
             if (oldConnection != null) {
@@ -2676,6 +2747,17 @@
         void onSessionLost(int token, @Nullable Exception exception);
     }
 
+    private static boolean isIPv6Only(List<LinkAddress> linkAddresses) {
+        boolean hasIPV6 = false;
+        boolean hasIPV4 = false;
+        for (final LinkAddress address : linkAddresses) {
+            hasIPV6 |= address.isIpv6();
+            hasIPV4 |= address.isIpv4();
+        }
+
+        return hasIPV6 && !hasIPV4;
+    }
+
     /**
      * Internal class managing IKEv2/IPsec VPN connectivity
      *
@@ -2723,7 +2805,7 @@
 
         @Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostFuture;
         @Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionFuture;
-
+        @Nullable private ScheduledFuture<?> mScheduledHandleDataStallFuture;
         /** Signal to ensure shutdown is honored even if a new Network is connected. */
         private boolean mIsRunning = true;
 
@@ -2750,6 +2832,14 @@
         private boolean mMobikeEnabled = false;
 
         /**
+         * The number of attempts to reset the IKE session since the last successful connection.
+         *
+         * <p>This variable controls the retry delay, and is reset when the VPN pass network
+         * validation.
+         */
+        private int mDataStallRetryCount = 0;
+
+        /**
          * The number of attempts since the last successful connection.
          *
          * <p>This variable controls the retry delay, and is reset when a new IKE session is
@@ -2881,15 +2971,27 @@
 
             try {
                 final String interfaceName = mTunnelIface.getInterfaceName();
-                final int maxMtu = mProfile.getMaxMtu();
                 final List<LinkAddress> internalAddresses = childConfig.getInternalAddresses();
                 final List<String> dnsAddrStrings = new ArrayList<>();
+                int vpnMtu;
+                vpnMtu = calculateVpnMtu();
+
+                // If the VPN is IPv6 only and its MTU is lower than 1280, mark the network as lost
+                // and send the VpnManager event to the VPN app.
+                if (isIPv6Only(internalAddresses) && vpnMtu < IPV6_MIN_MTU) {
+                    onSessionLost(
+                            token,
+                            new IkeIOException(
+                                    new IOException("No valid addresses for MTU < 1280")));
+                    return;
+                }
 
                 final Collection<RouteInfo> newRoutes = VpnIkev2Utils.getRoutesFromTrafficSelectors(
                         childConfig.getOutboundTrafficSelectors());
                 for (final LinkAddress address : internalAddresses) {
                     mTunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
                 }
+
                 for (InetAddress addr : childConfig.getInternalDnsServers()) {
                     dnsAddrStrings.add(addr.getHostAddress());
                 }
@@ -2907,7 +3009,7 @@
                     if (mVpnRunner != this) return;
 
                     mInterface = interfaceName;
-                    mConfig.mtu = maxMtu;
+                    mConfig.mtu = vpnMtu;
                     mConfig.interfaze = mInterface;
 
                     mConfig.addresses.clear();
@@ -2931,7 +3033,7 @@
                         if (isSettingsVpnLocked()) {
                             prepareStatusIntent();
                         }
-                        agentConnect();
+                        agentConnect(this::onValidationStatus);
                         return; // Link properties are already sent.
                     } else {
                         // Underlying networks also set in agentConnect()
@@ -3010,12 +3112,54 @@
                     // Ignore stale runner.
                     if (mVpnRunner != this) return;
 
+                    final LinkProperties oldLp = makeLinkProperties();
+
+                    final boolean underlyingNetworkHasChanged =
+                            !Arrays.equals(mConfig.underlyingNetworks, new Network[]{network});
                     mConfig.underlyingNetworks = new Network[] {network};
-                    mNetworkCapabilities =
-                            new NetworkCapabilities.Builder(mNetworkCapabilities)
-                                    .setUnderlyingNetworks(Collections.singletonList(network))
-                                    .build();
-                    doSetUnderlyingNetworks(mNetworkAgent, Collections.singletonList(network));
+                    mConfig.mtu = calculateVpnMtu();
+
+                    final LinkProperties newLp = makeLinkProperties();
+
+                    // If MTU is < 1280, IPv6 addresses will be removed. If there are no addresses
+                    // left (e.g. IPv6-only VPN network), mark VPN as having lost the session.
+                    if (newLp.getLinkAddresses().isEmpty()) {
+                        onSessionLost(
+                                token,
+                                new IkeIOException(
+                                        new IOException("No valid addresses for MTU < 1280")));
+                        return;
+                    }
+
+                    final Set<LinkAddress> removedAddrs = new HashSet<>(oldLp.getLinkAddresses());
+                    removedAddrs.removeAll(newLp.getLinkAddresses());
+
+                    // If addresses were removed despite no IKE config change, IPv6 addresses must
+                    // have been removed due to MTU size. Restart the VPN to ensure all IPv6
+                    // unconnected sockets on the new VPN network are closed and retried on the new
+                    // VPN network.
+                    if (!removedAddrs.isEmpty()) {
+                        startNewNetworkAgent(
+                                mNetworkAgent, "MTU too low for IPv6; restarting network agent");
+
+                        for (LinkAddress removed : removedAddrs) {
+                            mTunnelIface.removeAddress(
+                                    removed.getAddress(), removed.getPrefixLength());
+                        }
+                    } else {
+                        // Put below 3 updates into else block is because agentConnect() will do
+                        // those things, so there is no need to do the redundant work.
+                        if (!newLp.equals(oldLp)) doSendLinkProperties(mNetworkAgent, newLp);
+                        if (underlyingNetworkHasChanged) {
+                            mNetworkCapabilities =
+                                    new NetworkCapabilities.Builder(mNetworkCapabilities)
+                                            .setUnderlyingNetworks(
+                                                    Collections.singletonList(network))
+                                            .build();
+                            doSetUnderlyingNetworks(mNetworkAgent,
+                                    Collections.singletonList(network));
+                        }
+                    }
                 }
 
                 mTunnelIface.setUnderlyingNetwork(network);
@@ -3065,6 +3209,60 @@
             startOrMigrateIkeSession(network);
         }
 
+        @NonNull
+        private IkeSessionParams getIkeSessionParams(@NonNull Network underlyingNetwork) {
+            final IkeTunnelConnectionParams ikeTunConnParams =
+                    mProfile.getIkeTunnelConnectionParams();
+            if (ikeTunConnParams != null) {
+                final IkeSessionParams.Builder builder =
+                        new IkeSessionParams.Builder(ikeTunConnParams.getIkeSessionParams())
+                                .setNetwork(underlyingNetwork);
+                return builder.build();
+            } else {
+                return VpnIkev2Utils.buildIkeSessionParams(mContext, mProfile, underlyingNetwork);
+            }
+        }
+
+        @NonNull
+        private ChildSessionParams getChildSessionParams() {
+            final IkeTunnelConnectionParams ikeTunConnParams =
+                    mProfile.getIkeTunnelConnectionParams();
+            if (ikeTunConnParams != null) {
+                return ikeTunConnParams.getTunnelModeChildSessionParams();
+            } else {
+                return VpnIkev2Utils.buildChildSessionParams(mProfile.getAllowedAlgorithms());
+            }
+        }
+
+        private int calculateVpnMtu() {
+            final Network underlyingNetwork = mIkeConnectionInfo.getNetwork();
+            final LinkProperties lp = mConnectivityManager.getLinkProperties(underlyingNetwork);
+            if (underlyingNetwork == null || lp == null) {
+                // Return the max MTU defined in VpnProfile as the fallback option when there is no
+                // underlying network or LinkProperties is null.
+                return mProfile.getMaxMtu();
+            }
+
+            int underlyingMtu = lp.getMtu();
+
+            // Try to get MTU from kernel if MTU is not set in LinkProperties.
+            if (underlyingMtu == 0) {
+                try {
+                    underlyingMtu = mDeps.getJavaNetworkInterfaceMtu(lp.getInterfaceName(),
+                            mProfile.getMaxMtu());
+                } catch (SocketException e) {
+                    Log.d(TAG, "Got a SocketException when getting MTU from kernel: " + e);
+                    return mProfile.getMaxMtu();
+                }
+            }
+
+            return mDeps.calculateVpnMtu(
+                    getChildSessionParams().getSaProposals(),
+                    mProfile.getMaxMtu(),
+                    underlyingMtu,
+                    mIkeConnectionInfo.getLocalAddress() instanceof Inet4Address);
+        }
+
         /**
          * Start a new IKE session.
          *
@@ -3115,24 +3313,6 @@
                 // (non-default) network, and start the new one.
                 resetIkeState();
 
-                // Get Ike options from IkeTunnelConnectionParams if it's available in the
-                // profile.
-                final IkeTunnelConnectionParams ikeTunConnParams =
-                        mProfile.getIkeTunnelConnectionParams();
-                final IkeSessionParams ikeSessionParams;
-                final ChildSessionParams childSessionParams;
-                if (ikeTunConnParams != null) {
-                    final IkeSessionParams.Builder builder = new IkeSessionParams.Builder(
-                            ikeTunConnParams.getIkeSessionParams()).setNetwork(underlyingNetwork);
-                    ikeSessionParams = builder.build();
-                    childSessionParams = ikeTunConnParams.getTunnelModeChildSessionParams();
-                } else {
-                    ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(
-                            mContext, mProfile, underlyingNetwork);
-                    childSessionParams = VpnIkev2Utils.buildChildSessionParams(
-                            mProfile.getAllowedAlgorithms());
-                }
-
                 // TODO: Remove the need for adding two unused addresses with
                 // IPsec tunnels.
                 final InetAddress address = InetAddress.getLocalHost();
@@ -3150,8 +3330,8 @@
                 mSession =
                         mIkev2SessionCreator.createIkeSession(
                                 mContext,
-                                ikeSessionParams,
-                                childSessionParams,
+                                getIkeSessionParams(underlyingNetwork),
+                                getChildSessionParams(),
                                 mExecutor,
                                 new VpnIkev2Utils.IkeSessionCallbackImpl(
                                         TAG, IkeV2VpnRunner.this, token),
@@ -3200,18 +3380,52 @@
                     // Ignore stale runner.
                     if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
 
-                    // Handle the report only for current VPN network.
+                    // Handle the report only for current VPN network. If data stall is already
+                    // reported, ignoring the other reports. It means that the stall is not
+                    // recovered by MOBIKE and should be on the way to reset the ike session.
                     if (mNetworkAgent != null
-                            && mNetworkAgent.getNetwork().equals(report.getNetwork())) {
+                            && mNetworkAgent.getNetwork().equals(report.getNetwork())
+                            && !mDataStallSuspected) {
                         Log.d(TAG, "Data stall suspected");
 
                         // Trigger MOBIKE.
                         maybeMigrateIkeSession(mActiveNetwork);
+                        mDataStallSuspected = true;
                     }
                 }
             }
         }
 
+        public void onValidationStatus(int status) {
+            if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
+                // No data stall now. Reset it.
+                mExecutor.execute(() -> {
+                    mDataStallSuspected = false;
+                    mDataStallRetryCount = 0;
+                    if (mScheduledHandleDataStallFuture != null) {
+                        Log.d(TAG, "Recovered from stall. Cancel pending reset action.");
+                        mScheduledHandleDataStallFuture.cancel(false /* mayInterruptIfRunning */);
+                        mScheduledHandleDataStallFuture = null;
+                    }
+                });
+            } else {
+                // Skip other invalid status if the scheduled recovery exists.
+                if (mScheduledHandleDataStallFuture != null) return;
+
+                mScheduledHandleDataStallFuture = mExecutor.schedule(() -> {
+                    if (mDataStallSuspected) {
+                        Log.d(TAG, "Reset session to recover stalled network");
+                        // This will reset old state if it exists.
+                        startIkeSession(mActiveNetwork);
+                    }
+
+                    // Reset mScheduledHandleDataStallFuture since it's already run on executor
+                    // thread.
+                    mScheduledHandleDataStallFuture = null;
+                }, mDeps.getDataStallResetSessionSeconds(mDataStallRetryCount++), TimeUnit.SECONDS);
+            }
+        }
+
         /**
          * Handles loss of the default underlying network
          *
@@ -4339,6 +4553,7 @@
     // un-finalized.
     @VisibleForTesting
     public static class VpnNetworkAgentWrapper extends NetworkAgent {
+        private final ValidationStatusCallback mCallback;
         /** Create an VpnNetworkAgentWrapper */
         public VpnNetworkAgentWrapper(
                 @NonNull Context context,
@@ -4348,8 +4563,10 @@
                 @NonNull LinkProperties lp,
                 @NonNull NetworkScore score,
                 @NonNull NetworkAgentConfig config,
-                @Nullable NetworkProvider provider) {
+                @Nullable NetworkProvider provider,
+                @Nullable ValidationStatusCallback callback) {
             super(context, looper, logTag, nc, lp, score, config, provider);
+            mCallback = callback;
         }
 
         /** Update the LinkProperties */
@@ -4371,6 +4588,13 @@
         public void onNetworkUnwanted() {
             // We are user controlled, not driven by NetworkRequest.
         }
+
+        @Override
+        public void onValidationStatus(int status, Uri redirectUri) {
+            if (mCallback != null) {
+                mCallback.onValidationStatus(status);
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index a3b1a42..523a2dc 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -699,7 +699,6 @@
      */
     public float getNitsFromBacklight(float backlight) {
         if (mBacklightToNitsSpline == null) {
-            Slog.wtf(TAG, "requesting nits when no mapping exists.");
             return NITS_INVALID;
         }
         backlight = Math.max(backlight, mBacklightMinimum);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 7e80b7d..e907ebf 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -127,6 +127,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.AnimationThread;
 import com.android.server.DisplayThread;
@@ -151,6 +152,7 @@
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 
+
 /**
  * Manages attached displays.
  * <p>
@@ -1900,6 +1902,14 @@
                 if (displayDevice == null) {
                     return;
                 }
+                if (mLogicalDisplayMapper.getDisplayLocked(displayDevice)
+                        .getDisplayInfoLocked().type == Display.TYPE_INTERNAL) {
+                    FrameworkStatsLog.write(FrameworkStatsLog.BRIGHTNESS_CONFIGURATION_UPDATED,
+                                c.getCurve().first,
+                                c.getCurve().second,
+                                // should not be logged for virtual displays
+                                uniqueId);
+                }
                 mPersistentDataStore.setBrightnessConfigurationForDisplayLocked(c, displayDevice,
                         userSerial, packageName);
             } finally {
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index cd9ef09..c3313e0 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -245,6 +245,7 @@
 
                 if (mSentStartBroadcast) {
                     mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL);
+                    mSentStartBroadcast = false;
                 }
 
                 mActivityTaskManager.removeRootTasksWithActivityTypes(
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 6e2cceb..4ca4817 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -23,12 +23,14 @@
 
 import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.TaskInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -38,6 +40,8 @@
 import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
 import android.hardware.display.AmbientDisplayConfiguration;
+import android.net.Uri;
+import android.os.BatteryManager;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -72,6 +76,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -88,6 +94,15 @@
     private static final String DOZE_WAKE_LOCK_TAG = "dream:doze";
     private static final String DREAM_WAKE_LOCK_TAG = "dream:dream";
 
+    /** Constants for the when to activate dreams. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({DREAM_ON_DOCK, DREAM_ON_CHARGE, DREAM_ON_DOCK_OR_CHARGE})
+    public @interface WhenToDream {}
+    private static final int DREAM_DISABLED = 0x0;
+    private static final int DREAM_ON_DOCK = 0x1;
+    private static final int DREAM_ON_CHARGE = 0x2;
+    private static final int DREAM_ON_DOCK_OR_CHARGE = 0x3;
+
     private final Object mLock = new Object();
 
     private final Context mContext;
@@ -101,12 +116,20 @@
     private final DreamUiEventLogger mDreamUiEventLogger;
     private final ComponentName mAmbientDisplayComponent;
     private final boolean mDismissDreamOnActivityStart;
+    private final boolean mDreamsOnlyEnabledForSystemUser;
+    private final boolean mDreamsEnabledByDefaultConfig;
+    private final boolean mDreamsActivatedOnChargeByDefault;
+    private final boolean mDreamsActivatedOnDockByDefault;
 
     @GuardedBy("mLock")
     private DreamRecord mCurrentDream;
 
     private boolean mForceAmbientDisplayEnabled;
-    private final boolean mDreamsOnlyEnabledForSystemUser;
+    private SettingsObserver mSettingsObserver;
+    private boolean mDreamsEnabledSetting;
+    @WhenToDream private int mWhenToDream;
+    private boolean mIsDocked;
+    private boolean mIsCharging;
 
     // A temporary dream component that, when present, takes precedence over user configured dream
     // component.
@@ -144,6 +167,37 @@
                 }
             };
 
+    private final BroadcastReceiver mChargingReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mIsCharging = (BatteryManager.ACTION_CHARGING.equals(intent.getAction()));
+        }
+    };
+
+    private final BroadcastReceiver mDockStateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
+                int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
+                mIsDocked = dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+            }
+        }
+    };
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            synchronized (mLock) {
+                updateWhenToDreamSettings();
+            }
+        }
+    }
+
     public DreamManagerService(Context context) {
         super(context);
         mContext = context;
@@ -164,6 +218,14 @@
                 mContext.getResources().getBoolean(R.bool.config_dreamsOnlyEnabledForSystemUser);
         mDismissDreamOnActivityStart = mContext.getResources().getBoolean(
                 R.bool.config_dismissDreamOnActivityStart);
+
+        mDreamsEnabledByDefaultConfig = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_dreamsEnabledByDefault);
+        mDreamsActivatedOnChargeByDefault = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
+        mDreamsActivatedOnDockByDefault = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
+        mSettingsObserver = new SettingsObserver(mHandler);
     }
 
     @Override
@@ -197,6 +259,30 @@
                         DREAM_MANAGER_ORDERED_ID,
                         mActivityInterceptorCallback);
             }
+
+            mContext.registerReceiver(
+                    mDockStateReceiver, new IntentFilter(Intent.ACTION_DOCK_EVENT));
+            IntentFilter chargingIntentFilter = new IntentFilter();
+            chargingIntentFilter.addAction(BatteryManager.ACTION_CHARGING);
+            chargingIntentFilter.addAction(BatteryManager.ACTION_DISCHARGING);
+            mContext.registerReceiver(mChargingReceiver, chargingIntentFilter);
+
+            mSettingsObserver = new SettingsObserver(mHandler);
+            mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+                            Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP),
+                    false, mSettingsObserver, UserHandle.USER_ALL);
+            mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+                            Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK),
+                    false, mSettingsObserver, UserHandle.USER_ALL);
+            mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+                            Settings.Secure.SCREENSAVER_ENABLED),
+                    false, mSettingsObserver, UserHandle.USER_ALL);
+
+            // We don't get an initial broadcast for the batter state, so we have to initialize
+            // directly from BatteryManager.
+            mIsCharging = mContext.getSystemService(BatteryManager.class).isCharging();
+
+            updateWhenToDreamSettings();
         }
     }
 
@@ -207,6 +293,14 @@
             pw.println("mCurrentDream=" + mCurrentDream);
             pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled);
             pw.println("mDreamsOnlyEnabledForSystemUser=" + mDreamsOnlyEnabledForSystemUser);
+            pw.println("mDreamsEnabledSetting=" + mDreamsEnabledSetting);
+            pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled);
+            pw.println("mDreamsOnlyEnabledForSystemUser=" + mDreamsOnlyEnabledForSystemUser);
+            pw.println("mDreamsActivatedOnDockByDefault=" + mDreamsActivatedOnDockByDefault);
+            pw.println("mDreamsActivatedOnChargeByDefault=" + mDreamsActivatedOnChargeByDefault);
+            pw.println("mIsDocked=" + mIsDocked);
+            pw.println("mIsCharging=" + mIsCharging);
+            pw.println("mWhenToDream=" + mWhenToDream);
             pw.println("getDozeComponent()=" + getDozeComponent());
             pw.println();
 
@@ -214,7 +308,28 @@
         }
     }
 
-    /** Whether a real dream is occurring. */
+    private void updateWhenToDreamSettings() {
+        synchronized (mLock) {
+            final ContentResolver resolver = mContext.getContentResolver();
+
+            final int activateWhenCharging = (Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+                    mDreamsActivatedOnChargeByDefault ? 1 : 0,
+                    UserHandle.USER_CURRENT) != 0) ? DREAM_ON_CHARGE : DREAM_DISABLED;
+            final int activateWhenDocked = (Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+                    mDreamsActivatedOnDockByDefault ? 1 : 0,
+                    UserHandle.USER_CURRENT) != 0) ? DREAM_ON_DOCK : DREAM_DISABLED;
+            mWhenToDream = activateWhenCharging + activateWhenDocked;
+
+            mDreamsEnabledSetting = (Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.SCREENSAVER_ENABLED,
+                    mDreamsEnabledByDefaultConfig ? 1 : 0,
+                    UserHandle.USER_CURRENT) != 0);
+        }
+    }
+
+        /** Whether a real dream is occurring. */
     private boolean isDreamingInternal() {
         synchronized (mLock) {
             return mCurrentDream != null && !mCurrentDream.isPreview
@@ -236,6 +351,30 @@
         }
     }
 
+    /** Whether dreaming can start given user settings and the current dock/charge state. */
+    private boolean canStartDreamingInternal(boolean isScreenOn) {
+        synchronized (mLock) {
+            // Can't start dreaming if we are already dreaming.
+            if (isScreenOn && isDreamingInternal()) {
+                return false;
+            }
+
+            if (!mDreamsEnabledSetting) {
+                return false;
+            }
+
+            if ((mWhenToDream & DREAM_ON_CHARGE) == DREAM_ON_CHARGE) {
+                return mIsCharging;
+            }
+
+            if ((mWhenToDream & DREAM_ON_DOCK) == DREAM_ON_DOCK) {
+                return mIsDocked;
+            }
+
+            return false;
+        }
+    }
+
     protected void requestStartDreamFromShell() {
         requestDreamInternal();
     }
@@ -869,6 +1008,11 @@
         }
 
         @Override
+        public boolean canStartDreaming(boolean isScreenOn) {
+            return canStartDreamingInternal(isScreenOn);
+        }
+
+        @Override
         public ComponentName getActiveDreamComponent(boolean doze) {
             return getActiveDreamComponentInternal(doze);
         }
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 5253d34..d4e8f27 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -19,28 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
-import android.annotation.UserIdInt;
-import android.app.AppGlobals;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.TimeUtils;
-
-import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
 
 /**
  * Gets the service name using a framework resources, temporarily changing the service if necessary
@@ -48,259 +29,42 @@
  *
  * @hide
  */
-public final class FrameworkResourcesServiceNameResolver implements ServiceNameResolver {
+public final class FrameworkResourcesServiceNameResolver extends ServiceNameBaseResolver {
 
-    private static final String TAG = FrameworkResourcesServiceNameResolver.class.getSimpleName();
-
-    /** Handler message to {@link #resetTemporaryService(int)} */
-    private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
-
-    @NonNull
-    private final Context mContext;
-    @NonNull
-    private final Object mLock = new Object();
-    @StringRes
     private final int mStringResourceId;
     @ArrayRes
     private final int mArrayResourceId;
-    private final boolean mIsMultiple;
-    /**
-     * Map of temporary service name list set by {@link #setTemporaryServices(int, String[], int)},
-     * keyed by {@code userId}.
-     *
-     * <p>Typically used by Shell command and/or CTS tests to configure temporary services if
-     * mIsMultiple is true.
-     */
-    @GuardedBy("mLock")
-    private final SparseArray<String[]> mTemporaryServiceNamesList = new SparseArray<>();
-    /**
-     * Map of default services that have been disabled by
-     * {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}.
-     *
-     * <p>Typically used by Shell command and/or CTS tests.
-     */
-    @GuardedBy("mLock")
-    private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray();
-    @Nullable
-    private NameResolverListener mOnSetCallback;
-    /**
-     * When the temporary service will expire (and reset back to the default).
-     */
-    @GuardedBy("mLock")
-    private long mTemporaryServiceExpiration;
-
-    /**
-     * Handler used to reset the temporary service name.
-     */
-    @GuardedBy("mLock")
-    private Handler mTemporaryHandler;
 
     public FrameworkResourcesServiceNameResolver(@NonNull Context context,
             @StringRes int resourceId) {
-        mContext = context;
+        super(context, false);
         mStringResourceId = resourceId;
         mArrayResourceId = -1;
-        mIsMultiple = false;
     }
 
     public FrameworkResourcesServiceNameResolver(@NonNull Context context,
             @ArrayRes int resourceId, boolean isMultiple) {
+        super(context, isMultiple);
         if (!isMultiple) {
             throw new UnsupportedOperationException("Please use "
                     + "FrameworkResourcesServiceNameResolver(context, @StringRes int) constructor "
                     + "if single service mode is requested.");
         }
-        mContext = context;
         mStringResourceId = -1;
         mArrayResourceId = resourceId;
-        mIsMultiple = true;
     }
 
     @Override
-    public void setOnTemporaryServiceNameChangedCallback(@NonNull NameResolverListener callback) {
-        synchronized (mLock) {
-            this.mOnSetCallback = callback;
-        }
+    public String[] readServiceNameList(int userId) {
+        return mContext.getResources().getStringArray(mArrayResourceId);
     }
 
+    @Nullable
     @Override
-    public String getServiceName(@UserIdInt int userId) {
-        String[] serviceNames = getServiceNameList(userId);
-        return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
+    public String readServiceName(int userId) {
+        return mContext.getResources().getString(mStringResourceId);
     }
 
-    @Override
-    public String getDefaultServiceName(@UserIdInt int userId) {
-        String[] serviceNames = getDefaultServiceNameList(userId);
-        return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
-    }
-
-    /**
-     * Gets the default list of the service names for the given user.
-     *
-     * <p>Typically implemented by services which want to provide multiple backends.
-     */
-    @Override
-    public String[] getServiceNameList(int userId) {
-        synchronized (mLock) {
-            String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
-            if (temporaryNames != null) {
-                // Always log it, as it should only be used on CTS or during development
-                Slog.w(TAG, "getServiceName(): using temporary name "
-                        + Arrays.toString(temporaryNames) + " for user " + userId);
-                return temporaryNames;
-            }
-            final boolean disabled = mDefaultServicesDisabled.get(userId);
-            if (disabled) {
-                // Always log it, as it should only be used on CTS or during development
-                Slog.w(TAG, "getServiceName(): temporary name not set and default disabled for "
-                        + "user " + userId);
-                return null;
-            }
-            return getDefaultServiceNameList(userId);
-
-        }
-    }
-
-    /**
-     * Gets the default list of the service names for the given user.
-     *
-     * <p>Typically implemented by services which want to provide multiple backends.
-     */
-    @Override
-    public String[] getDefaultServiceNameList(int userId) {
-        synchronized (mLock) {
-            if (mIsMultiple) {
-                String[] serviceNameList = mContext.getResources().getStringArray(mArrayResourceId);
-                // Filter out unimplemented services
-                // Initialize the validated array as null because we do not know the final size.
-                List<String> validatedServiceNameList = new ArrayList<>();
-                try {
-                    for (int i = 0; i < serviceNameList.length; i++) {
-                        if (TextUtils.isEmpty(serviceNameList[i])) {
-                            continue;
-                        }
-                        ComponentName serviceComponent = ComponentName.unflattenFromString(
-                                serviceNameList[i]);
-                        ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
-                                serviceComponent,
-                                PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
-                        if (serviceInfo != null) {
-                            validatedServiceNameList.add(serviceNameList[i]);
-                        }
-                    }
-                } catch (Exception e) {
-                    Slog.e(TAG, "Could not validate provided services.", e);
-                }
-                String[] validatedServiceNameArray = new String[validatedServiceNameList.size()];
-                return validatedServiceNameList.toArray(validatedServiceNameArray);
-            } else {
-                final String name = mContext.getString(mStringResourceId);
-                return TextUtils.isEmpty(name) ? new String[0] : new String[]{name};
-            }
-        }
-    }
-
-    @Override
-    public boolean isConfiguredInMultipleMode() {
-        return mIsMultiple;
-    }
-
-    @Override
-    public boolean isTemporary(@UserIdInt int userId) {
-        synchronized (mLock) {
-            return mTemporaryServiceNamesList.get(userId) != null;
-        }
-    }
-
-    @Override
-    public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
-            int durationMs) {
-        setTemporaryServices(userId, new String[]{componentName}, durationMs);
-    }
-
-    @Override
-    public void setTemporaryServices(int userId, @NonNull String[] componentNames, int durationMs) {
-        synchronized (mLock) {
-            mTemporaryServiceNamesList.put(userId, componentNames);
-
-            if (mTemporaryHandler == null) {
-                mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
-                            synchronized (mLock) {
-                                resetTemporaryService(userId);
-                            }
-                        } else {
-                            Slog.wtf(TAG, "invalid handler msg: " + msg);
-                        }
-                    }
-                };
-            } else {
-                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
-            }
-            mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
-            mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
-            for (int i = 0; i < componentNames.length; i++) {
-                notifyTemporaryServiceNameChangedLocked(userId, componentNames[i],
-                        /* isTemporary= */ true);
-            }
-        }
-    }
-
-    @Override
-    public void resetTemporaryService(@UserIdInt int userId) {
-        synchronized (mLock) {
-            Slog.i(TAG, "resetting temporary service for user " + userId + " from "
-                    + Arrays.toString(mTemporaryServiceNamesList.get(userId)));
-            mTemporaryServiceNamesList.remove(userId);
-            if (mTemporaryHandler != null) {
-                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
-                mTemporaryHandler = null;
-            }
-            notifyTemporaryServiceNameChangedLocked(userId, /* newTemporaryName= */ null,
-                    /* isTemporary= */ false);
-        }
-    }
-
-    @Override
-    public boolean setDefaultServiceEnabled(int userId, boolean enabled) {
-        synchronized (mLock) {
-            final boolean currentlyEnabled = isDefaultServiceEnabledLocked(userId);
-            if (currentlyEnabled == enabled) {
-                Slog.i(TAG, "setDefaultServiceEnabled(" + userId + "): already " + enabled);
-                return false;
-            }
-            if (enabled) {
-                Slog.i(TAG, "disabling default service for user " + userId);
-                mDefaultServicesDisabled.removeAt(userId);
-            } else {
-                Slog.i(TAG, "enabling default service for user " + userId);
-                mDefaultServicesDisabled.put(userId, true);
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public boolean isDefaultServiceEnabled(int userId) {
-        synchronized (mLock) {
-            return isDefaultServiceEnabledLocked(userId);
-        }
-    }
-
-    private boolean isDefaultServiceEnabledLocked(int userId) {
-        return !mDefaultServicesDisabled.get(userId);
-    }
-
-    @Override
-    public String toString() {
-        synchronized (mLock) {
-            return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNamesList + "]";
-        }
-    }
 
     // TODO(b/117779333): support proto
     @Override
@@ -314,31 +78,4 @@
             pw.print(mDefaultServicesDisabled.size());
         }
     }
-
-    // TODO(b/117779333): support proto
-    @Override
-    public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
-        synchronized (mLock) {
-            final String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
-            if (temporaryNames != null) {
-                pw.print("tmpName=");
-                pw.print(Arrays.toString(temporaryNames));
-                final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
-                pw.print(" (expires in ");
-                TimeUtils.formatDuration(ttl, pw);
-                pw.print("), ");
-            }
-            pw.print("defaultName=");
-            pw.print(getDefaultServiceName(userId));
-            final boolean disabled = mDefaultServicesDisabled.get(userId);
-            pw.println(disabled ? " (disabled)" : " (enabled)");
-        }
-    }
-
-    private void notifyTemporaryServiceNameChangedLocked(@UserIdInt int userId,
-            @Nullable String newTemporaryName, boolean isTemporary) {
-        if (mOnSetCallback != null) {
-            mOnSetCallback.onNameResolved(userId, newTemporaryName, isTemporary);
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
index cac7f53..17d75e6 100644
--- a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
@@ -19,8 +19,11 @@
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
 
 import java.io.PrintWriter;
+import java.util.Set;
 
 /**
  * Gets the service name using a property from the {@link android.provider.Settings.Secure}
@@ -28,21 +31,34 @@
  *
  * @hide
  */
-public final class SecureSettingsServiceNameResolver implements ServiceNameResolver {
+public final class SecureSettingsServiceNameResolver extends ServiceNameBaseResolver {
+    /**
+     * The delimiter to be used to parse the secure settings string. Services must make sure
+     * that this delimiter is used while adding component names to their secure setting property.
+     */
+    private static final char COMPONENT_NAME_SEPARATOR = ':';
 
-    private final @NonNull Context mContext;
+    private final TextUtils.SimpleStringSplitter mStringColonSplitter =
+            new TextUtils.SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
 
     @NonNull
     private final String mProperty;
 
     public SecureSettingsServiceNameResolver(@NonNull Context context, @NonNull String property) {
-        mContext = context;
-        mProperty = property;
+        this(context, property, /*isMultiple*/false);
     }
 
-    @Override
-    public String getDefaultServiceName(@UserIdInt int userId) {
-        return Settings.Secure.getStringForUser(mContext.getContentResolver(), mProperty, userId);
+    /**
+     *
+     * @param context the context required to retrieve the secure setting value
+     * @param property name of the secure setting key
+     * @param isMultiple true if the system service using this resolver needs to connect to
+     *                   multiple remote services, false otherwise
+     */
+    public SecureSettingsServiceNameResolver(@NonNull Context context, @NonNull String property,
+            boolean isMultiple) {
+        super(context, isMultiple);
+        mProperty = property;
     }
 
     // TODO(b/117779333): support proto
@@ -61,4 +77,34 @@
     public String toString() {
         return "SecureSettingsServiceNameResolver[" + mProperty + "]";
     }
+
+    @Override
+    public String[] readServiceNameList(int userId) {
+        return parseColonDelimitedServiceNames(
+                Settings.Secure.getStringForUser(
+                        mContext.getContentResolver(), mProperty, userId));
+    }
+
+    @Override
+    public String readServiceName(int userId) {
+        return Settings.Secure.getStringForUser(
+                mContext.getContentResolver(), mProperty, userId);
+    }
+
+    private String[] parseColonDelimitedServiceNames(String serviceNames) {
+        final Set<String> delimitedServices = new ArraySet<>();
+        if (!TextUtils.isEmpty(serviceNames)) {
+            final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+            splitter.setString(serviceNames);
+            while (splitter.hasNext()) {
+                final String str = splitter.next();
+                if (TextUtils.isEmpty(str)) {
+                    continue;
+                }
+                delimitedServices.add(str);
+            }
+        }
+        String[] delimitedServicesArray = new String[delimitedServices.size()];
+        return delimitedServices.toArray(delimitedServicesArray);
+    }
 }
diff --git a/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java b/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java
new file mode 100644
index 0000000..76ea05e
--- /dev/null
+++ b/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.infra;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Gets the service name using a framework resources, temporarily changing the service if necessary
+ * (typically during CTS tests or service development).
+ *
+ * @hide
+ */
+public abstract class ServiceNameBaseResolver implements ServiceNameResolver {
+
+    private static final String TAG = ServiceNameBaseResolver.class.getSimpleName();
+
+    /** Handler message to {@link #resetTemporaryService(int)} */
+    private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
+
+    @NonNull
+    protected final Context mContext;
+    @NonNull
+    protected final Object mLock = new Object();
+
+    protected final boolean mIsMultiple;
+    /**
+     * Map of temporary service name list set by {@link #setTemporaryServices(int, String[], int)},
+     * keyed by {@code userId}.
+     *
+     * <p>Typically used by Shell command and/or CTS tests to configure temporary services if
+     * mIsMultiple is true.
+     */
+    @GuardedBy("mLock")
+    protected final SparseArray<String[]> mTemporaryServiceNamesList = new SparseArray<>();
+    /**
+     * Map of default services that have been disabled by
+     * {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}.
+     *
+     * <p>Typically used by Shell command and/or CTS tests.
+     */
+    @GuardedBy("mLock")
+    protected final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray();
+    @Nullable
+    private NameResolverListener mOnSetCallback;
+    /**
+     * When the temporary service will expire (and reset back to the default).
+     */
+    @GuardedBy("mLock")
+    private long mTemporaryServiceExpiration;
+
+    /**
+     * Handler used to reset the temporary service name.
+     */
+    @GuardedBy("mLock")
+    private Handler mTemporaryHandler;
+
+    protected ServiceNameBaseResolver(Context context, boolean isMultiple) {
+        mContext = context;
+        mIsMultiple = isMultiple;
+    }
+
+    @Override
+    public void setOnTemporaryServiceNameChangedCallback(@NonNull NameResolverListener callback) {
+        synchronized (mLock) {
+            this.mOnSetCallback = callback;
+        }
+    }
+
+    @Override
+    public String getServiceName(@UserIdInt int userId) {
+        String[] serviceNames = getServiceNameList(userId);
+        return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
+    }
+
+    @Override
+    public String getDefaultServiceName(@UserIdInt int userId) {
+        String[] serviceNames = getDefaultServiceNameList(userId);
+        return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
+    }
+
+    /**
+     * Gets the default list of the service names for the given user.
+     *
+     * <p>Typically implemented by services which want to provide multiple backends.
+     */
+    @Override
+    public String[] getServiceNameList(int userId) {
+        synchronized (mLock) {
+            String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
+            if (temporaryNames != null) {
+                // Always log it, as it should only be used on CTS or during development
+                Slog.w(TAG, "getServiceName(): using temporary name "
+                        + Arrays.toString(temporaryNames) + " for user " + userId);
+                return temporaryNames;
+            }
+            final boolean disabled = mDefaultServicesDisabled.get(userId);
+            if (disabled) {
+                // Always log it, as it should only be used on CTS or during development
+                Slog.w(TAG, "getServiceName(): temporary name not set and default disabled for "
+                        + "user " + userId);
+                return null;
+            }
+            return getDefaultServiceNameList(userId);
+
+        }
+    }
+
+    /**
+     * Base classes must override this to read from the desired config e.g. framework resource,
+     * secure settings etc.
+     */
+    @Nullable
+    public abstract String[] readServiceNameList(int userId);
+
+    /**
+     * Base classes must override this to read from the desired config e.g. framework resource,
+     * secure settings etc.
+     */
+    @Nullable
+    public abstract String readServiceName(int userId);
+
+    /**
+     * Gets the default list of the service names for the given user.
+     *
+     * <p>Typically implemented by services which want to provide multiple backends.
+     */
+    @Override
+    public String[] getDefaultServiceNameList(int userId) {
+        synchronized (mLock) {
+            if (mIsMultiple) {
+                String[] serviceNameList = readServiceNameList(userId);
+                // Filter out unimplemented services
+                // Initialize the validated array as null because we do not know the final size.
+                List<String> validatedServiceNameList = new ArrayList<>();
+                try {
+                    for (int i = 0; i < serviceNameList.length; i++) {
+                        if (TextUtils.isEmpty(serviceNameList[i])) {
+                            continue;
+                        }
+                        ComponentName serviceComponent = ComponentName.unflattenFromString(
+                                serviceNameList[i]);
+                        ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
+                                serviceComponent,
+                                PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+                        if (serviceInfo != null) {
+                            validatedServiceNameList.add(serviceNameList[i]);
+                        }
+                    }
+                } catch (Exception e) {
+                    Slog.e(TAG, "Could not validate provided services.", e);
+                }
+                String[] validatedServiceNameArray = new String[validatedServiceNameList.size()];
+                return validatedServiceNameList.toArray(validatedServiceNameArray);
+            } else {
+                final String name = readServiceName(userId);
+                return TextUtils.isEmpty(name) ? new String[0] : new String[]{name};
+            }
+        }
+    }
+
+    @Override
+    public boolean isConfiguredInMultipleMode() {
+        return mIsMultiple;
+    }
+
+    @Override
+    public boolean isTemporary(@UserIdInt int userId) {
+        synchronized (mLock) {
+            return mTemporaryServiceNamesList.get(userId) != null;
+        }
+    }
+
+    @Override
+    public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
+            int durationMs) {
+        setTemporaryServices(userId, new String[]{componentName}, durationMs);
+    }
+
+    @Override
+    public void setTemporaryServices(int userId, @NonNull String[] componentNames, int durationMs) {
+        synchronized (mLock) {
+            mTemporaryServiceNamesList.put(userId, componentNames);
+
+            if (mTemporaryHandler == null) {
+                mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+                    @Override
+                    public void handleMessage(Message msg) {
+                        if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+                            synchronized (mLock) {
+                                resetTemporaryService(userId);
+                            }
+                        } else {
+                            Slog.wtf(TAG, "invalid handler msg: " + msg);
+                        }
+                    }
+                };
+            } else {
+                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+            }
+            mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
+            mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
+            for (int i = 0; i < componentNames.length; i++) {
+                notifyTemporaryServiceNameChangedLocked(userId, componentNames[i],
+                        /* isTemporary= */ true);
+            }
+        }
+    }
+
+    @Override
+    public void resetTemporaryService(@UserIdInt int userId) {
+        synchronized (mLock) {
+            Slog.i(TAG, "resetting temporary service for user " + userId + " from "
+                    + Arrays.toString(mTemporaryServiceNamesList.get(userId)));
+            mTemporaryServiceNamesList.remove(userId);
+            if (mTemporaryHandler != null) {
+                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+                mTemporaryHandler = null;
+            }
+            notifyTemporaryServiceNameChangedLocked(userId, /* newTemporaryName= */ null,
+                    /* isTemporary= */ false);
+        }
+    }
+
+    @Override
+    public boolean setDefaultServiceEnabled(int userId, boolean enabled) {
+        synchronized (mLock) {
+            final boolean currentlyEnabled = isDefaultServiceEnabledLocked(userId);
+            if (currentlyEnabled == enabled) {
+                Slog.i(TAG, "setDefaultServiceEnabled(" + userId + "): already " + enabled);
+                return false;
+            }
+            if (enabled) {
+                Slog.i(TAG, "disabling default service for user " + userId);
+                mDefaultServicesDisabled.removeAt(userId);
+            } else {
+                Slog.i(TAG, "enabling default service for user " + userId);
+                mDefaultServicesDisabled.put(userId, true);
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean isDefaultServiceEnabled(int userId) {
+        synchronized (mLock) {
+            return isDefaultServiceEnabledLocked(userId);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean isDefaultServiceEnabledLocked(int userId) {
+        return !mDefaultServicesDisabled.get(userId);
+    }
+
+    @Override
+    public String toString() {
+        synchronized (mLock) {
+            return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNamesList + "]";
+        }
+    }
+
+    // TODO(b/117779333): support proto
+    @Override
+    public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
+        synchronized (mLock) {
+            final String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
+            if (temporaryNames != null) {
+                pw.print("tmpName=");
+                pw.print(Arrays.toString(temporaryNames));
+                final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
+                pw.print(" (expires in ");
+                TimeUtils.formatDuration(ttl, pw);
+                pw.print("), ");
+            }
+            pw.print("defaultName=");
+            pw.print(getDefaultServiceName(userId));
+            final boolean disabled = mDefaultServicesDisabled.get(userId);
+            pw.println(disabled ? " (disabled)" : " (enabled)");
+        }
+    }
+
+    private void notifyTemporaryServiceNameChangedLocked(@UserIdInt int userId,
+            @Nullable String newTemporaryName, boolean isTemporary) {
+        if (mOnSetCallback != null) {
+            mOnSetCallback.onNameResolved(userId, newTemporaryName, isTemporary);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/input/BatteryController.java b/services/core/java/com/android/server/input/BatteryController.java
index 36199de..9d4f181 100644
--- a/services/core/java/com/android/server/input/BatteryController.java
+++ b/services/core/java/com/android/server/input/BatteryController.java
@@ -371,6 +371,17 @@
         }
     }
 
+    public void notifyStylusGestureStarted(int deviceId, long eventTime) {
+        synchronized (mLock) {
+            final DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
+            if (monitor == null) {
+                return;
+            }
+
+            monitor.onStylusGestureStarted(eventTime);
+        }
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         synchronized (mLock) {
             final String indent = prefix + "  ";
@@ -557,6 +568,8 @@
 
         public void onTimeout(long eventTime) {}
 
+        public void onStylusGestureStarted(long eventTime) {}
+
         // Returns the current battery state that can be used to notify listeners BatteryController.
         public State getBatteryStateForReporting() {
             return new State(mState);
@@ -600,6 +613,22 @@
         }
 
         @Override
+        public void onStylusGestureStarted(long eventTime) {
+            processChangesAndNotify(eventTime, (time) -> {
+                final boolean wasValid = mValidityTimeoutCallback != null;
+                if (!wasValid && mState.capacity == 0.f) {
+                    // Handle a special case where the USI device reports a battery capacity of 0
+                    // at boot until the first battery update. To avoid wrongly sending out a
+                    // battery capacity of 0 if we detect stylus presence before the capacity
+                    // is first updated, do not validate the battery state when the state is not
+                    // valid and the capacity is 0.
+                    return;
+                }
+                markUsiBatteryValid();
+            });
+        }
+
+        @Override
         public void onTimeout(long eventTime) {
             processChangesAndNotify(eventTime, (time) -> markUsiBatteryInvalid());
         }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 69b0e65..31f63d8 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -19,6 +19,8 @@
 import static android.provider.DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT;
 import static android.view.KeyEvent.KEYCODE_UNKNOWN;
 
+import android.Manifest;
+import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
@@ -2671,6 +2673,12 @@
         mBatteryController.unregisterBatteryListener(deviceId, listener, Binder.getCallingPid());
     }
 
+    @EnforcePermission(Manifest.permission.BLUETOOTH)
+    @Override
+    public String getInputDeviceBluetoothAddress(int deviceId) {
+        return mNative.getBluetoothAddress(deviceId);
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -3052,6 +3060,12 @@
                 com.android.internal.R.bool.config_perDisplayFocusEnabled);
     }
 
+    // Native callback.
+    @SuppressWarnings("unused")
+    private void notifyStylusGestureStarted(int deviceId, long eventTime) {
+        mBatteryController.notifyStylusGestureStarted(deviceId, eventTime);
+    }
+
     /**
      * Flatten a map into a string list, with value positioned directly next to the
      * key.
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 63c0a88..cfa7fb1 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -204,6 +204,9 @@
     /** Set the displayId on which the mouse cursor should be shown. */
     void setPointerDisplayId(int displayId);
 
+    /** Get the bluetooth address of an input device if known, otherwise return null. */
+    String getBluetoothAddress(int deviceId);
+
     /** The native implementation of InputManagerService methods. */
     class NativeImpl implements NativeInputManagerService {
         /** Pointer to native input manager service object, used by native code. */
@@ -418,5 +421,8 @@
 
         @Override
         public native void setPointerDisplayId(int displayId);
+
+        @Override
+        public native String getBluetoothAddress(int deviceId);
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 76495b1..0eaa5e4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -144,6 +144,7 @@
 import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.inputmethod.DirectBootAwareness;
@@ -1592,8 +1593,13 @@
         private final InputMethodManagerService mService;
 
         public Lifecycle(Context context) {
+            this(context, new InputMethodManagerService(context));
+        }
+
+        public Lifecycle(
+                Context context, @NonNull InputMethodManagerService inputMethodManagerService) {
             super(context);
-            mService = new InputMethodManagerService(context);
+            mService = inputMethodManagerService;
         }
 
         @Override
@@ -1668,12 +1674,25 @@
     }
 
     public InputMethodManagerService(Context context) {
+        this(context, null, null);
+    }
+
+    @VisibleForTesting
+    InputMethodManagerService(
+            Context context,
+            @Nullable ServiceThread serviceThreadForTesting,
+            @Nullable InputMethodBindingController bindingControllerForTesting) {
         mContext = context;
         mRes = context.getResources();
         // 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 */);
+        final ServiceThread thread =
+                serviceThreadForTesting != null
+                        ? serviceThreadForTesting
+                        : new ServiceThread(
+                                HANDLER_THREAD_NAME,
+                                Process.THREAD_PRIORITY_FOREGROUND,
+                                true /* allowIo */);
         thread.start();
         mHandler = Handler.createAsync(thread.getLooper(), this);
         // Note: SettingsObserver doesn't register observers in its constructor.
@@ -1701,10 +1720,13 @@
 
         updateCurrentProfileIds();
         AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
-        mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
-                mSettings, context);
+        mSwitchingController =
+                InputMethodSubtypeSwitchingController.createInstanceLocked(mSettings, context);
         mMenuController = new InputMethodMenuController(this);
-        mBindingController = new InputMethodBindingController(this);
+        mBindingController =
+                bindingControllerForTesting != null
+                        ? bindingControllerForTesting
+                        : new InputMethodBindingController(this);
         mAutofillController = new AutofillSuggestionsController(this);
         mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
                 com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
@@ -3673,6 +3695,7 @@
         // UI for input.
         if (isTextEditor && editorInfo != null
                 && shouldRestoreImeVisibility(windowToken, softInputMode)) {
+            if (DEBUG) Slog.v(TAG, "Will show input to restore visibility");
             res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection,
                     editorInfo, startInputFlags, startInputReason, unverifiedTargetSdkVersion,
                     imeDispatcher);
@@ -3719,11 +3742,17 @@
                                 imeDispatcher);
                         didStart = true;
                     }
-                    showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+                    showCurrentInputLocked(
+                            windowToken,
+                            InputMethodManager.SHOW_IMPLICIT,
+                            null,
                             SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV);
                 }
                 break;
             case LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
+                if (DEBUG) {
+                    Slog.v(TAG, "Window asks to keep the input in whatever state it was last in");
+                }
                 // Do nothing.
                 break;
             case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
@@ -3794,6 +3823,7 @@
                     // To maintain compatibility, we are now hiding the IME when we don't have
                     // an editor upon refocusing a window.
                     if (startInputByWinGainedFocus) {
+                        if (DEBUG) Slog.v(TAG, "Same window without editor will hide input");
                         hideCurrentInputLocked(mCurFocusedWindow, 0, null,
                                 SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
                     }
@@ -3807,6 +3837,7 @@
                     // 1) SOFT_INPUT_STATE_UNCHANGED state without an editor
                     // 2) SOFT_INPUT_STATE_VISIBLE state without an editor
                     // 3) SOFT_INPUT_STATE_ALWAYS_VISIBLE state without an editor
+                    if (DEBUG) Slog.v(TAG, "Window without editor will hide input");
                     hideCurrentInputLocked(mCurFocusedWindow, 0, null,
                             SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR);
                 }
@@ -4601,12 +4632,6 @@
             }
             if (!setVisible) {
                 if (mCurClient != null) {
-                    // IMMS only knows of focused window, not the actual IME target.
-                    // e.g. it isn't aware of any window that has both
-                    // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target.
-                    // Send it to window manager to hide IME from IME target window.
-                    // TODO(b/139861270): send to mCurClient.client once IMMS is aware of
-                    // actual IME target.
                     mWindowManagerInternal.hideIme(
                             mHideRequestWindowMap.get(windowToken),
                             mCurClient.mSelfReportedDisplayId);
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 364f6db..4ce0320 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -364,16 +364,19 @@
         // 1.) A normal, non-privileged app querying its own locale.
         // 2.) The installer of the given app querying locales of a package installed by said
         // installer.
-        // 3.) The current input method querying locales of another package.
+        // 3.) The current input method querying locales of the current foreground app.
         // 4.) A privileged system service querying locales of another package.
         // The least privileged case is a normal app performing a query, so check that first and get
         // locales if the package name is owned by the app. Next check if the calling app is the
         // installer of the given app and get locales. Finally check if the calling app is the
-        // current input method. If neither conditions matched, check if the caller has the
-        // necessary permission and fetch locales.
+        // current input method, and that app is querying locales of the current foreground app. If
+        // neither conditions matched, check if the caller has the necessary permission and fetch
+        // locales.
         if (!isPackageOwnedByCaller(appPackageName, userId)
                 && !isCallerInstaller(appPackageName, userId)
-                && !isCallerFromCurrentInputMethod(userId)) {
+                && !(isCallerFromCurrentInputMethod(userId)
+                    && mActivityManagerInternal.isAppForeground(
+                            getPackageUid(appPackageName, userId)))) {
             enforceReadAppSpecificLocalesPermission();
         }
         final long token = Binder.clearCallingIdentity();
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 51851be..6232028 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -129,7 +129,7 @@
             new RemoteCallbackList<>();
 
     // Proxy object to communicate with the Context Hub HAL
-    private IContextHubWrapper mContextHubWrapper;
+    private final IContextHubWrapper mContextHubWrapper;
 
     // The manager for transaction queue
     private ContextHubTransactionManager mTransactionManager;
@@ -210,8 +210,24 @@
     }
 
     public ContextHubService(Context context, IContextHubWrapper contextHubWrapper) {
+        Log.i(TAG, "Starting Context Hub Service init");
         mContext = context;
-        init(contextHubWrapper, /* isFirstInit= */ true);
+        long startTimeNs = SystemClock.elapsedRealtimeNanos();
+        mContextHubWrapper = contextHubWrapper;
+        if (!initContextHubServiceState(startTimeNs)) {
+            Log.e(TAG, "Failed to initialize the Context Hub Service");
+            return;
+        }
+        initDefaultClientMap();
+
+        initLocationSettingNotifications();
+        initWifiSettingNotifications();
+        initAirplaneModeSettingNotifications();
+        initMicrophoneSettingNotifications();
+        initBtSettingNotifications();
+
+        scheduleDailyMetricSnapshot();
+        Log.i(TAG, "Finished Context Hub Service init");
     }
 
     /**
@@ -293,11 +309,10 @@
      * Initializes the private state of the ContextHubService
      *
      * @param startTimeNs               the start time when init was called
-     * @param isFirstInit               if true, this is the first time init is called - boot time
      *
      * @return      if mContextHubWrapper is not null and a full state init was done
      */
-    private boolean initContextHubServiceState(long startTimeNs, boolean isFirstInit) {
+    private boolean initContextHubServiceState(long startTimeNs) {
         if (mContextHubWrapper == null) {
             mTransactionManager = null;
             mClientManager = null;
@@ -317,12 +332,10 @@
             hubInfo = new Pair(Collections.emptyList(), Collections.emptyList());
         }
 
-        if (isFirstInit) {
-            long bootTimeNs = SystemClock.elapsedRealtimeNanos() - startTimeNs;
-            int numContextHubs = hubInfo.first.size();
-            ContextHubStatsLog.write(ContextHubStatsLog.CONTEXT_HUB_BOOTED, bootTimeNs,
-                    numContextHubs);
-        }
+        long bootTimeNs = SystemClock.elapsedRealtimeNanos() - startTimeNs;
+        int numContextHubs = hubInfo.first.size();
+        ContextHubStatsLog.write(ContextHubStatsLog.CONTEXT_HUB_BOOTED, bootTimeNs,
+                numContextHubs);
 
         mContextHubIdToInfoMap = Collections.unmodifiableMap(
                 ContextHubServiceUtil.createContextHubInfoMap(hubInfo.first));
@@ -749,31 +762,6 @@
     }
 
     /**
-     * Handles a service restart or service init for the first time
-     *
-     * @param contextHubWrapper         the Context Hub wrapper
-     * @param isFirstInit               if true, this is the first time init is called - boot time
-     */
-    private void init(IContextHubWrapper contextHubWrapper, boolean isFirstInit) {
-        Log.i(TAG, "Starting Context Hub Service init");
-        long startTimeNs = SystemClock.elapsedRealtimeNanos();
-        mContextHubWrapper = contextHubWrapper;
-        if (!initContextHubServiceState(startTimeNs, isFirstInit)) {
-            Log.e(TAG, "Failed to initialize the Context Hub Service");
-            return;
-        }
-        initDefaultClientMap();
-
-        initLocationSettingNotifications();
-        initWifiSettingNotifications();
-        initAirplaneModeSettingNotifications();
-        initMicrophoneSettingNotifications();
-        initBtSettingNotifications();
-
-        scheduleDailyMetricSnapshot();
-    }
-
-    /**
      * Handles a unicast or broadcast message from a nanoapp.
      *
      * @param contextHubId the ID of the hub the message came from
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index f653b93..c67d54f 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -132,7 +132,7 @@
                 }
             }
 
-            mEventLogger.log(new EventLogger.StringEvent("mScreenOnOffReceiver", null));
+            mEventLogger.enqueue(new EventLogger.StringEvent("mScreenOnOffReceiver", null));
         }
     };
 
@@ -151,7 +151,7 @@
         mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter);
     }
 
-    // Methods that implement MediaRouter2 operations.
+    // Start of methods that implement MediaRouter2 operations.
 
     @NonNull
     public void enforceMediaContentControlPermission() {
@@ -199,46 +199,6 @@
         }
     }
 
-    @NonNull
-    public RoutingSessionInfo getSystemSessionInfo(
-            @Nullable String packageName, boolean setDeviceRouteSelected) {
-        final int uid = Binder.getCallingUid();
-        final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
-        final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-                == PackageManager.PERMISSION_GRANTED;
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            RoutingSessionInfo systemSessionInfo = null;
-            synchronized (mLock) {
-                UserRecord userRecord = getOrCreateUserRecordLocked(userId);
-                List<RoutingSessionInfo> sessionInfos;
-                if (hasModifyAudioRoutingPermission) {
-                    if (setDeviceRouteSelected) {
-                        systemSessionInfo = userRecord.mHandler.mSystemProvider
-                                .generateDeviceRouteSelectedSessionInfo(packageName);
-                    } else {
-                        sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos();
-                        if (sessionInfos != null && !sessionInfos.isEmpty()) {
-                            systemSessionInfo = new RoutingSessionInfo.Builder(sessionInfos.get(0))
-                                    .setClientPackageName(packageName).build();
-                        } else {
-                            Slog.w(TAG, "System provider does not have any session info.");
-                        }
-                    }
-                } else {
-                    systemSessionInfo = new RoutingSessionInfo.Builder(
-                            userRecord.mHandler.mSystemProvider.getDefaultSessionInfo())
-                            .setClientPackageName(packageName).build();
-                }
-            }
-            return systemSessionInfo;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
     public void registerRouter2(@NonNull IMediaRouter2 router, @NonNull String packageName) {
         Objects.requireNonNull(router, "router must not be null");
         if (TextUtils.isEmpty(packageName)) {
@@ -418,7 +378,9 @@
         }
     }
 
-    // Methods that implement MediaRouter2Manager operations.
+    // End of methods that implement MediaRouter2 operations.
+
+    // Start of methods that implement MediaRouter2Manager operations.
 
     @NonNull
     public List<RoutingSessionInfo> getRemoteSessions(@NonNull IMediaRouter2Manager manager) {
@@ -610,6 +572,52 @@
         }
     }
 
+    // End of methods that implement MediaRouter2Manager operations.
+
+    // Start of methods that implements operations for both MediaRouter2 and MediaRouter2Manager.
+
+    @NonNull
+    public RoutingSessionInfo getSystemSessionInfo(
+            @Nullable String packageName, boolean setDeviceRouteSelected) {
+        final int uid = Binder.getCallingUid();
+        final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+        final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+                == PackageManager.PERMISSION_GRANTED;
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            RoutingSessionInfo systemSessionInfo = null;
+            synchronized (mLock) {
+                UserRecord userRecord = getOrCreateUserRecordLocked(userId);
+                List<RoutingSessionInfo> sessionInfos;
+                if (hasModifyAudioRoutingPermission) {
+                    if (setDeviceRouteSelected) {
+                        systemSessionInfo = userRecord.mHandler.mSystemProvider
+                                .generateDeviceRouteSelectedSessionInfo(packageName);
+                    } else {
+                        sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos();
+                        if (sessionInfos != null && !sessionInfos.isEmpty()) {
+                            systemSessionInfo = new RoutingSessionInfo.Builder(sessionInfos.get(0))
+                                    .setClientPackageName(packageName).build();
+                        } else {
+                            Slog.w(TAG, "System provider does not have any session info.");
+                        }
+                    }
+                } else {
+                    systemSessionInfo = new RoutingSessionInfo.Builder(
+                            userRecord.mHandler.mSystemProvider.getDefaultSessionInfo())
+                            .setClientPackageName(packageName).build();
+                }
+            }
+            return systemSessionInfo;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // End of methods that implements operations for both MediaRouter2 and MediaRouter2Manager.
+
     public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
         pw.println(prefix + "MediaRouter2ServiceImpl");
 
@@ -634,7 +642,7 @@
     /* package */ void updateRunningUserAndProfiles(int newActiveUserId) {
         synchronized (mLock) {
             if (mCurrentActiveUserId != newActiveUserId) {
-                mEventLogger.log(
+                mEventLogger.enqueue(
                         EventLogger.StringEvent.from("switchUser",
                                 "userId: %d", newActiveUserId));
 
@@ -678,6 +686,8 @@
         return mUserManagerInternal.getProfileParentId(userId) == mCurrentActiveUserId;
     }
 
+    // Start of locked methods that are used by MediaRouter2.
+
     @GuardedBy("mLock")
     private void registerRouter2Locked(@NonNull IMediaRouter2 router, int uid, int pid,
             @NonNull String packageName, int userId, boolean hasConfigureWifiDisplayPermission,
@@ -705,7 +715,7 @@
                 obtainMessage(UserHandler::notifyRouterRegistered,
                         userRecord.mHandler, routerRecord));
 
-        mEventLogger.log(EventLogger.StringEvent.from("registerRouter2",
+        mEventLogger.enqueue(EventLogger.StringEvent.from("registerRouter2",
                 "package: %s, uid: %d, pid: %d, router id: %d",
                 packageName, uid, pid, routerRecord.mRouterId));
     }
@@ -718,7 +728,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from(
                         "unregisterRouter2",
                         "package: %s, router id: %d",
@@ -744,7 +754,7 @@
             return;
         }
 
-        mEventLogger.log(EventLogger.StringEvent.from(
+        mEventLogger.enqueue(EventLogger.StringEvent.from(
                 "setDiscoveryRequestWithRouter2",
                 "router id: %d, discovery request: %s",
                 routerRecord.mRouterId, discoveryRequest.toString()));
@@ -766,7 +776,7 @@
         RouterRecord routerRecord = mAllRouterRecords.get(binder);
 
         if (routerRecord != null) {
-            mEventLogger.log(EventLogger.StringEvent.from(
+            mEventLogger.enqueue(EventLogger.StringEvent.from(
                     "setRouteVolumeWithRouter2",
                     "router id: %d, volume: %d",
                     routerRecord.mRouterId, volume));
@@ -851,7 +861,7 @@
             return;
         }
 
-        mEventLogger.log(EventLogger.StringEvent.from(
+        mEventLogger.enqueue(EventLogger.StringEvent.from(
                 "selectRouteWithRouter2",
                 "router id: %d, route: %s",
                 routerRecord.mRouterId, route.getId()));
@@ -871,7 +881,7 @@
             return;
         }
 
-        mEventLogger.log(EventLogger.StringEvent.from(
+        mEventLogger.enqueue(EventLogger.StringEvent.from(
                 "deselectRouteWithRouter2",
                 "router id: %d, route: %s",
                 routerRecord.mRouterId, route.getId()));
@@ -891,7 +901,7 @@
             return;
         }
 
-        mEventLogger.log(EventLogger.StringEvent.from(
+        mEventLogger.enqueue(EventLogger.StringEvent.from(
                 "transferToRouteWithRouter2",
                 "router id: %d, route: %s",
                 routerRecord.mRouterId, route.getId()));
@@ -921,7 +931,7 @@
             return;
         }
 
-        mEventLogger.log(EventLogger.StringEvent.from(
+        mEventLogger.enqueue(EventLogger.StringEvent.from(
                 "setSessionVolumeWithRouter2",
                 "router id: %d, session: %s, volume: %d",
                 routerRecord.mRouterId,  uniqueSessionId, volume));
@@ -941,7 +951,7 @@
             return;
         }
 
-        mEventLogger.log(EventLogger.StringEvent.from(
+        mEventLogger.enqueue(EventLogger.StringEvent.from(
                 "releaseSessionWithRouter2",
                 "router id: %d, session: %s",
                 routerRecord.mRouterId,  uniqueSessionId));
@@ -952,6 +962,10 @@
                         DUMMY_REQUEST_ID, routerRecord, uniqueSessionId));
     }
 
+    // End of locked methods that are used by MediaRouter2.
+
+    // Start of locked methods that are used by MediaRouter2Manager.
+
     private List<RoutingSessionInfo> getRemoteSessionsLocked(
             @NonNull IMediaRouter2Manager manager) {
         final IBinder binder = manager.asBinder();
@@ -983,7 +997,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from("registerManager",
                         "uid: %d, pid: %d, package: %s, userId: %d",
                         uid, pid, packageName, userId));
@@ -1025,7 +1039,7 @@
         }
         UserRecord userRecord = managerRecord.mUserRecord;
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from(
                         "unregisterManager",
                         "package: %s, userId: %d, managerId: %d",
@@ -1045,7 +1059,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from("startScan",
                         "manager: %d", managerRecord.mManagerId));
 
@@ -1059,7 +1073,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from("stopScan",
                         "manager: %d", managerRecord.mManagerId));
 
@@ -1076,7 +1090,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from("setRouteVolumeWithManager",
                         "managerId: %d, routeId: %s, volume: %d",
                         managerRecord.mManagerId, route.getId(), volume));
@@ -1096,7 +1110,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from("requestCreateSessionWithManager",
                         "managerId: %d, routeId: %s",
                         managerRecord.mManagerId, route.getId()));
@@ -1146,7 +1160,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from("selectRouteWithManager",
                         "managerId: %d, session: %s, routeId: %s",
                         managerRecord.mManagerId, uniqueSessionId, route.getId()));
@@ -1172,7 +1186,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from("deselectRouteWithManager",
                         "managerId: %d, session: %s, routeId: %s",
                         managerRecord.mManagerId, uniqueSessionId, route.getId()));
@@ -1198,7 +1212,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from("transferToRouteWithManager",
                         "managerId: %d, session: %s, routeId: %s",
                         managerRecord.mManagerId, uniqueSessionId, route.getId()));
@@ -1224,7 +1238,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from("setSessionVolumeWithManager",
                         "managerId: %d, session: %s, volume: %d",
                         managerRecord.mManagerId, uniqueSessionId, volume));
@@ -1245,7 +1259,7 @@
             return;
         }
 
-        mEventLogger.log(
+        mEventLogger.enqueue(
                 EventLogger.StringEvent.from("releaseSessionWithManager",
                         "managerId: %d, session: %s",
                         managerRecord.mManagerId, uniqueSessionId));
@@ -1260,6 +1274,10 @@
                         uniqueRequestId, routerRecord, uniqueSessionId));
     }
 
+    // End of locked methods that are used by MediaRouter2Manager.
+
+    // Start of locked methods that are used by both MediaRouter2 and MediaRouter2Manager.
+
     @GuardedBy("mLock")
     private UserRecord getOrCreateUserRecordLocked(int userId) {
         UserRecord userRecord = mUserRecords.get(userId);
@@ -1294,6 +1312,8 @@
         }
     }
 
+    // End of locked methods that are used by both MediaRouter2 and MediaRouter2Manager.
+
     static long toUniqueRequestId(int requesterId, int originalRequestId) {
         return ((long) requesterId << 32) | originalRequestId;
     }
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index 4c21195..2e67bf2 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -22,6 +22,15 @@
 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__BOOT;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__USER_CREATED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__USER_DELETED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__COMPAT_CHANGED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_ADDED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_DELETED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_REPLACED;
 import static com.android.server.pm.AppsFilterUtils.canQueryAsInstaller;
 import static com.android.server.pm.AppsFilterUtils.canQueryViaComponents;
 import static com.android.server.pm.AppsFilterUtils.canQueryViaPackage;
@@ -36,6 +45,7 @@
 import android.content.pm.SigningDetails;
 import android.content.pm.UserInfo;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -49,6 +59,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.FgThread;
 import com.android.server.compat.CompatChange;
 import com.android.server.om.OverlayReferenceMapper;
@@ -351,8 +362,15 @@
             if (pkg == null) {
                 return;
             }
+            final long currentTimeUs = SystemClock.currentTimeMicro();
             updateEnabledState(pkg);
             mAppsFilter.updateShouldFilterCacheForPackage(snapshot, packageName);
+            mAppsFilter.logCacheUpdated(
+                    PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__COMPAT_CHANGED,
+                    SystemClock.currentTimeMicro() - currentTimeUs,
+                    snapshot.getUserInfos().length,
+                    snapshot.getPackageStates().size(),
+                    pkg.getUid());
         }
 
         private void updateEnabledState(@NonNull AndroidPackage pkg) {
@@ -465,7 +483,8 @@
         mOverlayReferenceMapper.rebuildIfDeferred();
         mFeatureConfig.onSystemReady();
 
-        updateEntireShouldFilterCacheAsync(pmInternal);
+        updateEntireShouldFilterCacheAsync(pmInternal,
+                PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__BOOT);
     }
 
     /**
@@ -473,16 +492,23 @@
      *
      * @param newPkgSetting the new setting being added
      * @param isReplace     if the package is being replaced and may need extra cleanup.
+     * @param retainImplicitGrantOnReplace {@code true} to retain implicit grant access if
+     *                                     the package is being replaced.
      */
     public void addPackage(Computer snapshot, PackageStateInternal newPkgSetting,
-            boolean isReplace) {
+            boolean isReplace, boolean retainImplicitGrantOnReplace) {
+        final long currentTimeUs = SystemClock.currentTimeMicro();
+        final int logType = isReplace
+                ? PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_REPLACED
+                : PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_ADDED;
         if (DEBUG_TRACING) {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
         }
         try {
             if (isReplace) {
                 // let's first remove any prior rules for this package
-                removePackage(snapshot, newPkgSetting, true /*isReplace*/);
+                removePackageInternal(snapshot, newPkgSetting,
+                        true /*isReplace*/, retainImplicitGrantOnReplace);
             }
             final ArrayMap<String, ? extends PackageStateInternal> settings =
                     snapshot.getPackageStates();
@@ -508,6 +534,8 @@
                         }
                     }
                 }
+                logCacheUpdated(logType, SystemClock.currentTimeMicro() - currentTimeUs,
+                        users.length, settings.size(), newPkgSetting.getAppId());
             } else {
                 invalidateCache("addPackage: " + newPkgSetting.getPackageName());
             }
@@ -757,18 +785,19 @@
         }
     }
 
-    private void updateEntireShouldFilterCacheAsync(PackageManagerInternal pmInternal) {
-        updateEntireShouldFilterCacheAsync(pmInternal, CACHE_REBUILD_DELAY_MIN_MS);
+    private void updateEntireShouldFilterCacheAsync(PackageManagerInternal pmInternal, int reason) {
+        updateEntireShouldFilterCacheAsync(pmInternal, CACHE_REBUILD_DELAY_MIN_MS, reason);
     }
 
     private void updateEntireShouldFilterCacheAsync(PackageManagerInternal pmInternal,
-            long delayMs) {
+            long delayMs, int reason) {
         mBackgroundHandler.postDelayed(() -> {
             if (!mCacheValid.compareAndSet(CACHE_INVALID, CACHE_VALID)) {
                 // Cache is already valid.
                 return;
             }
 
+            final long currentTimeUs = SystemClock.currentTimeMicro();
             final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>();
             final UserInfo[][] usersRef = new UserInfo[1][];
             final Computer snapshot = (Computer) pmInternal.snapshot();
@@ -787,11 +816,13 @@
 
             updateEntireShouldFilterCacheInner(snapshot, settings, usersRef[0], USER_ALL);
             onChanged();
+            logCacheRebuilt(reason, SystemClock.currentTimeMicro() - currentTimeUs,
+                    users.length, settings.size());
 
             if (!mCacheValid.compareAndSet(CACHE_VALID, CACHE_VALID)) {
                 Slog.i(TAG, "Cache invalidated while building, retrying.");
                 updateEntireShouldFilterCacheAsync(pmInternal,
-                        Math.min(delayMs * 2, CACHE_REBUILD_DELAY_MAX_MS));
+                        Math.min(delayMs * 2, CACHE_REBUILD_DELAY_MAX_MS), reason);
                 return;
             }
 
@@ -803,15 +834,27 @@
         if (!mCacheReady) {
             return;
         }
+        final long currentTimeUs = SystemClock.currentTimeMicro();
         updateEntireShouldFilterCache(snapshot, newUserId);
+        logCacheRebuilt(
+                PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__USER_CREATED,
+                SystemClock.currentTimeMicro() - currentTimeUs,
+                snapshot.getUserInfos().length,
+                snapshot.getPackageStates().size());
     }
 
-    public void onUserDeleted(@UserIdInt int userId) {
+    public void onUserDeleted(Computer snapshot, @UserIdInt int userId) {
         if (!mCacheReady) {
             return;
         }
+        final long currentTimeUs = SystemClock.currentTimeMicro();
         removeShouldFilterCacheForUser(userId);
         onChanged();
+        logCacheRebuilt(
+                PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__USER_DELETED,
+                SystemClock.currentTimeMicro() - currentTimeUs,
+                snapshot.getUserInfos().length,
+                snapshot.getPackageStates().size());
     }
 
     private void updateShouldFilterCacheForPackage(Computer snapshot,
@@ -976,13 +1019,31 @@
     }
 
     /**
-     * Equivalent to calling {@link #addPackage(Computer, PackageStateInternal, boolean)}
-     * with {@code isReplace} equal to {@code false}.
+     * Equivalent to calling {@link #addPackage(Computer, PackageStateInternal, boolean, boolean)}
+     * with {@code isReplace} and {@code retainImplicitGrantOnReplace} equal to {@code false}.
      *
-     * @see AppsFilterImpl#addPackage(Computer, PackageStateInternal, boolean)
+     * @see AppsFilterImpl#addPackage(Computer, PackageStateInternal, boolean, boolean)
      */
     public void addPackage(Computer snapshot, PackageStateInternal newPkgSetting) {
-        addPackage(snapshot, newPkgSetting, false /* isReplace */);
+        addPackage(snapshot, newPkgSetting, false /* isReplace */,
+                false /* retainImplicitGrantOnReplace */);
+    }
+
+    /**
+     * Removes a package for consideration when filtering visibility between apps.
+     *
+     * @param setting the setting of the package being removed.
+     */
+    public void removePackage(Computer snapshot, PackageStateInternal setting) {
+        final long currentTimeUs = SystemClock.currentTimeMicro();
+        removePackageInternal(snapshot, setting,
+                false /* isReplace */, false /* retainImplicitGrantOnReplace */);
+        logCacheUpdated(
+                PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_DELETED,
+                SystemClock.currentTimeMicro() - currentTimeUs,
+                snapshot.getUserInfos().length,
+                snapshot.getPackageStates().size(),
+                setting.getAppId());
     }
 
     /**
@@ -990,33 +1051,37 @@
      *
      * @param setting   the setting of the package being removed.
      * @param isReplace if the package is being replaced.
+     * @param retainImplicitGrantOnReplace {@code true} to retain implicit grant access if
+     *                                     the package is being replaced.
      */
-    public void removePackage(Computer snapshot, PackageStateInternal setting,
-            boolean isReplace) {
+    private void removePackageInternal(Computer snapshot, PackageStateInternal setting,
+            boolean isReplace, boolean retainImplicitGrantOnReplace) {
         final ArraySet<String> additionalChangedPackages;
         final ArrayMap<String, ? extends PackageStateInternal> settings =
                 snapshot.getPackageStates();
         final UserInfo[] users = snapshot.getUserInfos();
         final Collection<SharedUserSetting> sharedUserSettings = snapshot.getAllSharedUsers();
         final int userCount = users.length;
-        synchronized (mImplicitlyQueryableLock) {
-            for (int u = 0; u < userCount; u++) {
-                final int userId = users[u].id;
-                final int removingUid = UserHandle.getUid(userId, setting.getAppId());
-                mImplicitlyQueryable.remove(removingUid);
-                for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
-                    mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i),
-                            removingUid);
-                }
+        if (!isReplace || !retainImplicitGrantOnReplace) {
+            synchronized (mImplicitlyQueryableLock) {
+                for (int u = 0; u < userCount; u++) {
+                    final int userId = users[u].id;
+                    final int removingUid = UserHandle.getUid(userId, setting.getAppId());
+                    mImplicitlyQueryable.remove(removingUid);
+                    for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
+                        mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i),
+                                removingUid);
+                    }
 
-                if (isReplace) {
-                    continue;
-                }
+                    if (isReplace) {
+                        continue;
+                    }
 
-                mRetainedImplicitlyQueryable.remove(removingUid);
-                for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) {
-                    mRetainedImplicitlyQueryable.remove(
-                            mRetainedImplicitlyQueryable.keyAt(i), removingUid);
+                    mRetainedImplicitlyQueryable.remove(removingUid);
+                    for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) {
+                        mRetainedImplicitlyQueryable.remove(
+                                mRetainedImplicitlyQueryable.keyAt(i), removingUid);
+                    }
                 }
             }
         }
@@ -1174,4 +1239,18 @@
             }
         }
     }
+
+    private void logCacheRebuilt(int eventId, long latency, int userCount, int packageCount) {
+        FrameworkStatsLog.write(PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED,
+                eventId, latency, userCount, packageCount, mShouldFilterCache.size());
+    }
+
+    private void logCacheUpdated(int eventId, long latency, int userCount, int packageCount,
+            int appId) {
+        if (!mCacheReady) {
+            return;
+        }
+        FrameworkStatsLog.write(PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED,
+                eventId, appId, latency, userCount, packageCount, mShouldFilterCache.size());
+    }
 }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index e7412c5..d856d54 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -410,7 +410,7 @@
                                 job.jobFinished(params, !completed);
                             } else {
                                 // Periodic job
-                                job.jobFinished(params, true);
+                                job.jobFinished(params, false /* reschedule */);
                             }
                             markDexOptCompleted();
                         }
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
new file mode 100644
index 0000000..df95f86
--- /dev/null
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -0,0 +1,130 @@
+/*
+ * 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.pm;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.IBackgroundInstallControlService;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.SparseArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemService;
+
+/**
+ * @hide
+ */
+public class BackgroundInstallControlService extends SystemService {
+    private static final String TAG = "BackgroundInstallControlService";
+
+    private final Context mContext;
+    private final BinderService mBinderService;
+    private final IPackageManager mIPackageManager;
+
+    // User ID -> package name -> time diff
+    // The time diff between the last foreground activity installer and
+    // the "onPackageAdded" function call.
+    private final SparseArrayMap<String, Long> mBackgroundInstalledPackages =
+            new SparseArrayMap<>();
+
+    public BackgroundInstallControlService(@NonNull Context context) {
+        this(new InjectorImpl(context));
+    }
+
+    @VisibleForTesting
+    BackgroundInstallControlService(@NonNull Injector injector) {
+        super(injector.getContext());
+        mContext = injector.getContext();
+        mIPackageManager = injector.getIPackageManager();
+        mBinderService = new BinderService(this);
+    }
+
+    private static final class BinderService extends IBackgroundInstallControlService.Stub {
+        final BackgroundInstallControlService mService;
+
+        BinderService(BackgroundInstallControlService service)  {
+            mService = service;
+        }
+
+        @Override
+        public ParceledListSlice<PackageInfo> getBackgroundInstalledPackages(
+                @PackageManager.PackageInfoFlagsBits long flags, int userId) {
+            ParceledListSlice<PackageInfo> packages;
+            try {
+                packages = mService.mIPackageManager.getInstalledPackages(flags, userId);
+            } catch (RemoteException e) {
+                throw new IllegalStateException("Package manager not available", e);
+            }
+
+            // TODO(b/244216300): to enable the test the usage by BinaryTransparencyService,
+            // we currently comment out the actual implementation.
+            // The fake implementation is just to filter out the first app of the list.
+            // for (int i = 0, size = packages.getList().size(); i < size; i++) {
+            //     String packageName = packages.getList().get(i).packageName;
+            //     if (!mBackgroundInstalledPackages.contains(userId, packageName) {
+            //         packages.getList().remove(i);
+            //     }
+            // }
+            if (packages.getList().size() > 0) {
+                packages.getList().remove(0);
+            }
+            return packages;
+        }
+    }
+
+    /**
+     * Called when the system service should publish a binder service using
+     * {@link #publishBinderService(String, IBinder).}
+     */
+    @Override
+    public void onStart() {
+        publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService);
+    }
+
+    /**
+     * Dependency injector for {@link #BackgroundInstallControlService)}.
+     */
+    interface Injector {
+        Context getContext();
+
+        IPackageManager getIPackageManager();
+    }
+
+    private static final class InjectorImpl implements Injector {
+        private final Context mContext;
+
+        InjectorImpl(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
+
+        @Override
+        public IPackageManager getIPackageManager() {
+            return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 9d007c9..7ea0c04 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -463,7 +463,8 @@
 
             final Computer snapshot = mPm.snapshotComputer();
             mPm.mComponentResolver.addAllComponents(pkg, chatty, mPm.mSetupWizardPackage, snapshot);
-            mPm.mAppsFilter.addPackage(snapshot, pkgSetting, isReplace);
+            mPm.mAppsFilter.addPackage(snapshot, pkgSetting, isReplace,
+                    (scanFlags & SCAN_DONT_KILL_APP) != 0 /* retainImplicitGrantOnReplace */);
             mPm.addAllPackageProperties(pkg);
 
             if (oldPkgSetting == null || oldPkgSetting.getPkg() == null) {
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 8534fab..84324f2 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -3,8 +3,6 @@
 jsharkey@android.com
 jsharkey@google.com
 narayan@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
 include /PACKAGE_MANAGER_OWNERS
 
 # apex support
@@ -26,16 +24,10 @@
 per-file PackageUsage.java = file:dex/OWNERS
 
 # multi user / cross profile
-per-file CrossProfileAppsServiceImpl.java = omakoto@google.com, yamasani@google.com
-per-file CrossProfileAppsService.java = omakoto@google.com, yamasani@google.com
-per-file CrossProfileIntentFilter.java = omakoto@google.com, yamasani@google.com
-per-file CrossProfileIntentResolver.java = omakoto@google.com, yamasani@google.com
+per-file CrossProfile* = file:MULTIUSER_AND_ENTERPRISE_OWNERS
 per-file RestrictionsSet.java = file:MULTIUSER_AND_ENTERPRISE_OWNERS
-per-file UserManager* = file:/MULTIUSER_OWNERS
 per-file UserRestriction* = file:MULTIUSER_AND_ENTERPRISE_OWNERS
-per-file UserSystemPackageInstaller* = file:/MULTIUSER_OWNERS
-per-file UserTypeDetails.java = file:/MULTIUSER_OWNERS
-per-file UserTypeFactory.java = file:/MULTIUSER_OWNERS
+per-file User* = file:/MULTIUSER_OWNERS
 
 # security
 per-file KeySetHandle.java = cbrubaker@google.com, nnk@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5df73a6..72ec510 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2763,10 +2763,11 @@
                     "Missing existing base package");
         }
 
-        // Default to require only if existing base apk has fs-verity.
+        // Default to require only if existing base apk has fs-verity signature.
         mVerityFoundForApks = PackageManagerServiceUtils.isApkVerityEnabled()
                 && params.mode == SessionParams.MODE_INHERIT_EXISTING
-                && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
+                && (new File(VerityUtils.getFsveritySignatureFilePath(
+                        pkgInfo.applicationInfo.getBaseCodePath()))).exists();
 
         final List<File> removedFiles = getRemovedFilesLocked();
         final List<String> removeSplitList = new ArrayList<>();
@@ -3326,7 +3327,7 @@
                     "Failure to obtain package info.");
         }
         final List<String> filePaths = packageLite.getAllApkPaths();
-        final String appLabel = mPreapprovalDetails.getLabel();
+        final CharSequence appLabel = mPreapprovalDetails.getLabel();
         final ULocale appLocale = mPreapprovalDetails.getLocale();
         final ApplicationInfo appInfo = packageInfo.applicationInfo;
         boolean appLabelMatched = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 68019da..b979e7a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1146,7 +1146,7 @@
         var done = SystemClock.currentTimeMicro();
 
         if (mSnapshotStatistics != null) {
-            mSnapshotStatistics.rebuild(now, done, hits);
+            mSnapshotStatistics.rebuild(now, done, hits, newSnapshot.getPackageStates().size());
         }
         return newSnapshot;
     }
@@ -4217,7 +4217,7 @@
             mSettings.removeUserLPw(userId);
             mPendingBroadcasts.remove(userId);
             mDeletePackageHelper.removeUnusedPackagesLPw(userManager, userId);
-            mAppsFilter.onUserDeleted(userId);
+            mAppsFilter.onUserDeleted(snapshotComputer(), userId);
         }
         mInstantAppRegistry.onUserRemoved(userId);
     }
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index bbc4fde..7e93673 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -308,7 +308,7 @@
                 mPm.mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName);
                 final Computer snapshot = mPm.snapshotComputer();
                 mPm.mAppsFilter.removePackage(snapshot,
-                        snapshot.getPackageStateInternal(packageName), false /* isReplace */);
+                        snapshot.getPackageStateInternal(packageName));
                 removedAppId = mPm.mSettings.removePackageLPw(packageName);
                 if (outInfo != null) {
                     outInfo.mRemovedAppId = removedAppId;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 12c9d4b..45c0d6e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -455,19 +455,24 @@
     // The user's preferred activities associated with particular intent
     // filters.
     @Watched
-    private final WatchedSparseArray<PreferredIntentResolver>
-            mPreferredActivities = new WatchedSparseArray<>();
+    private final WatchedSparseArray<PreferredIntentResolver> mPreferredActivities;
+    private final SnapshotCache<WatchedSparseArray<PreferredIntentResolver>>
+            mPreferredActivitiesSnapshot;
 
     // The persistent preferred activities of the user's profile/device owner
     // associated with particular intent filters.
     @Watched
     private final WatchedSparseArray<PersistentPreferredIntentResolver>
-            mPersistentPreferredActivities = new WatchedSparseArray<>();
+            mPersistentPreferredActivities;
+    private final SnapshotCache<WatchedSparseArray<PersistentPreferredIntentResolver>>
+            mPersistentPreferredActivitiesSnapshot;
+
 
     // For every user, it is used to find to which other users the intent can be forwarded.
     @Watched
-    private final WatchedSparseArray<CrossProfileIntentResolver>
-            mCrossProfileIntentResolvers = new WatchedSparseArray<>();
+    private final WatchedSparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers;
+    private final SnapshotCache<WatchedSparseArray<CrossProfileIntentResolver>>
+            mCrossProfileIntentResolversSnapshot;
 
     @Watched
     final WatchedArrayMap<String, SharedUserSetting> mSharedUsers = new WatchedArrayMap<>();
@@ -476,11 +481,12 @@
 
     // For reading/writing settings file.
     @Watched
-    private final WatchedArrayList<Signature> mPastSignatures =
-            new WatchedArrayList<Signature>();
+    private final WatchedArrayList<Signature> mPastSignatures;
+    private final SnapshotCache<WatchedArrayList<Signature>> mPastSignaturesSnapshot;
+
     @Watched
-    private final WatchedArrayMap<Long, Integer> mKeySetRefs =
-            new WatchedArrayMap<Long, Integer>();
+    private final WatchedArrayMap<Long, Integer> mKeySetRefs;
+    private final SnapshotCache<WatchedArrayMap<Long, Integer>> mKeySetRefsSnapshot;
 
     // Packages that have been renamed since they were first installed.
     // Keys are the new names of the packages, values are the original
@@ -511,7 +517,8 @@
      * scanning to make it less confusing.
      */
     @Watched
-    private final WatchedArrayList<PackageSetting> mPendingPackages = new WatchedArrayList<>();
+    private final WatchedArrayList<PackageSetting> mPendingPackages;
+    private final SnapshotCache<WatchedArrayList<PackageSetting>> mPendingPackagesSnapshot;
 
     private final File mSystemDir;
 
@@ -583,6 +590,26 @@
         mInstallerPackagesSnapshot =
                 new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages,
                                          "Settings.mInstallerPackages");
+        mPreferredActivities = new WatchedSparseArray<>();
+        mPreferredActivitiesSnapshot = new SnapshotCache.Auto<>(mPreferredActivities,
+                mPreferredActivities, "Settings.mPreferredActivities");
+        mPersistentPreferredActivities = new WatchedSparseArray<>();
+        mPersistentPreferredActivitiesSnapshot = new SnapshotCache.Auto<>(
+                mPersistentPreferredActivities, mPersistentPreferredActivities,
+                "Settings.mPersistentPreferredActivities");
+        mCrossProfileIntentResolvers = new WatchedSparseArray<>();
+        mCrossProfileIntentResolversSnapshot = new SnapshotCache.Auto<>(
+                mCrossProfileIntentResolvers, mCrossProfileIntentResolvers,
+                "Settings.mCrossProfileIntentResolvers");
+        mPastSignatures = new WatchedArrayList<>();
+        mPastSignaturesSnapshot = new SnapshotCache.Auto<>(mPastSignatures, mPastSignatures,
+                "Settings.mPastSignatures");
+        mKeySetRefs = new WatchedArrayMap<>();
+        mKeySetRefsSnapshot = new SnapshotCache.Auto<>(mKeySetRefs, mKeySetRefs,
+                "Settings.mKeySetRefs");
+        mPendingPackages = new WatchedArrayList<>();
+        mPendingPackagesSnapshot = new SnapshotCache.Auto<>(mPendingPackages, mPendingPackages,
+                "Settings.mPendingPackages");
         mKeySetManagerService = new KeySetManagerService(mPackages);
 
         // Test-only handler working on background thread.
@@ -623,6 +650,26 @@
         mInstallerPackagesSnapshot =
                 new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages,
                                          "Settings.mInstallerPackages");
+        mPreferredActivities = new WatchedSparseArray<>();
+        mPreferredActivitiesSnapshot = new SnapshotCache.Auto<>(mPreferredActivities,
+                mPreferredActivities, "Settings.mPreferredActivities");
+        mPersistentPreferredActivities = new WatchedSparseArray<>();
+        mPersistentPreferredActivitiesSnapshot = new SnapshotCache.Auto<>(
+                mPersistentPreferredActivities, mPersistentPreferredActivities,
+                "Settings.mPersistentPreferredActivities");
+        mCrossProfileIntentResolvers = new WatchedSparseArray<>();
+        mCrossProfileIntentResolversSnapshot = new SnapshotCache.Auto<>(
+                mCrossProfileIntentResolvers, mCrossProfileIntentResolvers,
+                "Settings.mCrossProfileIntentResolvers");
+        mPastSignatures = new WatchedArrayList<>();
+        mPastSignaturesSnapshot = new SnapshotCache.Auto<>(mPastSignatures, mPastSignatures,
+                "Settings.mPastSignatures");
+        mKeySetRefs = new WatchedArrayMap<>();
+        mKeySetRefsSnapshot = new SnapshotCache.Auto<>(mKeySetRefs, mKeySetRefs,
+                "Settings.mKeySetRefs");
+        mPendingPackages = new WatchedArrayList<>();
+        mPendingPackagesSnapshot = new SnapshotCache.Auto<>(mPendingPackages, mPendingPackages,
+                "Settings.mPendingPackages");
         mKeySetManagerService = new KeySetManagerService(mPackages);
 
         mHandler = handler;
@@ -699,24 +746,27 @@
         mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages);
         mVersion.putAll(r.mVersion);
         mVerifierDeviceIdentity = r.mVerifierDeviceIdentity;
-        WatchedSparseArray.snapshot(
-                mPreferredActivities, r.mPreferredActivities);
-        WatchedSparseArray.snapshot(
-                mPersistentPreferredActivities, r.mPersistentPreferredActivities);
-        WatchedSparseArray.snapshot(
-                mCrossProfileIntentResolvers, r.mCrossProfileIntentResolvers);
+        mPreferredActivities = r.mPreferredActivitiesSnapshot.snapshot();
+        mPreferredActivitiesSnapshot = new SnapshotCache.Sealed<>();
+        mPersistentPreferredActivities = r.mPersistentPreferredActivitiesSnapshot.snapshot();
+        mPersistentPreferredActivitiesSnapshot = new SnapshotCache.Sealed<>();
+        mCrossProfileIntentResolvers = r.mCrossProfileIntentResolversSnapshot.snapshot();
+        mCrossProfileIntentResolversSnapshot = new SnapshotCache.Sealed<>();
+
         mSharedUsers.snapshot(r.mSharedUsers);
         mAppIds = r.mAppIds.snapshot();
-        WatchedArrayList.snapshot(
-                mPastSignatures, r.mPastSignatures);
-        WatchedArrayMap.snapshot(
-                mKeySetRefs, r.mKeySetRefs);
+
+        mPastSignatures = r.mPastSignaturesSnapshot.snapshot();
+        mPastSignaturesSnapshot = new SnapshotCache.Sealed<>();
+        mKeySetRefs = r.mKeySetRefsSnapshot.snapshot();
+        mKeySetRefsSnapshot = new SnapshotCache.Sealed<>();
+
         mRenamedPackages.snapshot(r.mRenamedPackages);
         mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration);
         mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp);
         // mReadMessages
-        WatchedArrayList.snapshot(
-                mPendingPackages, r.mPendingPackages);
+        mPendingPackages = r.mPendingPackagesSnapshot.snapshot();
+        mPendingPackagesSnapshot = new SnapshotCache.Sealed<>();
         mSystemDir = null;
         // mKeySetManagerService;
         mPermissions = r.mPermissions;
diff --git a/services/core/java/com/android/server/pm/SnapshotStatistics.java b/services/core/java/com/android/server/pm/SnapshotStatistics.java
index 2cfc894..e04a1e5 100644
--- a/services/core/java/com/android/server/pm/SnapshotStatistics.java
+++ b/services/core/java/com/android/server/pm/SnapshotStatistics.java
@@ -24,11 +24,13 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.Locale;
+import java.util.concurrent.TimeUnit;
 
 /**
  * This class records statistics about PackageManagerService snapshots.  It maintains two sets of
@@ -59,9 +61,9 @@
     public static final int SNAPSHOT_TICK_INTERVAL_MS = 60 * 1000;
 
     /**
-     * The number of ticks for long statistics.  This is one week.
+     * The interval of the snapshot statistics logging.
      */
-    public static final int SNAPSHOT_LONG_TICKS = 7 * 24 * 60;
+    private static final long SNAPSHOT_LOG_INTERVAL_US = TimeUnit.DAYS.toMicros(1);
 
     /**
      * The number snapshot event logs that can be generated in a single logging interval.
@@ -93,6 +95,28 @@
     public static final int SNAPSHOT_SHORT_LIFETIME = 5;
 
     /**
+     *  Buckets to represent a range of the rebuild latency for the histogram of
+     *  snapshot rebuild latency.
+     */
+    private static final int REBUILD_LATENCY_BUCKET_LESS_THAN_1_MILLIS = 1;
+    private static final int REBUILD_LATENCY_BUCKET_LESS_THAN_2_MILLIS = 2;
+    private static final int REBUILD_LATENCY_BUCKET_LESS_THAN_5_MILLIS = 5;
+    private static final int REBUILD_LATENCY_BUCKET_LESS_THAN_10_MILLIS = 10;
+    private static final int REBUILD_LATENCY_BUCKET_LESS_THAN_20_MILLIS = 20;
+    private static final int REBUILD_LATENCY_BUCKET_LESS_THAN_50_MILLIS = 50;
+    private static final int REBUILD_LATENCY_BUCKET_LESS_THAN_100_MILLIS = 100;
+
+    /**
+     *  Buckets to represent a range of the reuse count for the histogram of
+     *  snapshot reuse counts.
+     */
+    private static final int REUSE_COUNT_BUCKET_LESS_THAN_1 = 1;
+    private static final int REUSE_COUNT_BUCKET_LESS_THAN_10 = 10;
+    private static final int REUSE_COUNT_BUCKET_LESS_THAN_100 = 100;
+    private static final int REUSE_COUNT_BUCKET_LESS_THAN_1000 = 1000;
+    private static final int REUSE_COUNT_BUCKET_LESS_THAN_10000 = 10000;
+
+    /**
      * The lock to control access to this object.
      */
     private final Object mLock = new Object();
@@ -113,11 +137,6 @@
     private int mEventsReported = 0;
 
     /**
-     * The tick counter.  At the default tick interval, this wraps every 4000 years or so.
-     */
-    private int mTicks = 0;
-
-    /**
      * The handler used for the periodic ticks.
      */
     private Handler mHandler = null;
@@ -139,8 +158,6 @@
 
         // The number of bins
         private int mCount;
-        // The mapping of low integers to bins
-        private int[] mBinMap;
         // The maximum mapped value.  Values at or above this are mapped to the
         // top bin.
         private int mMaxBin;
@@ -158,16 +175,6 @@
             mCount = mUserKey.length + 1;
             // The maximum value is one more than the last one in the map.
             mMaxBin = mUserKey[mUserKey.length - 1] + 1;
-            mBinMap = new int[mMaxBin + 1];
-
-            int j = 0;
-            for (int i = 0; i < mUserKey.length; i++) {
-                while (j <= mUserKey[i]) {
-                    mBinMap[j] = i;
-                    j++;
-                }
-            }
-            mBinMap[mMaxBin] = mUserKey.length;
         }
 
         /**
@@ -175,9 +182,14 @@
          */
         public int getBin(int x) {
             if (x >= 0 && x < mMaxBin) {
-                return mBinMap[x];
+                for (int i = 0; i < mUserKey.length; i++) {
+                    if (x <= mUserKey[i]) {
+                        return i;
+                    }
+                }
+                return 0; // should not happen
             } else if (x >= mMaxBin) {
-                return mBinMap[mMaxBin];
+                return mUserKey.length;
             } else {
                 // x is negative.  The bin will not be used.
                 return 0;
@@ -263,6 +275,11 @@
         public int mMaxBuildTimeUs = 0;
 
         /**
+         * The maximum used count since the last log.
+         */
+        public int mMaxUsedCount = 0;
+
+        /**
          * Record the rebuild.  The parameters are the length of time it took to build the
          * latest snapshot, and the number of times the _previous_ snapshot was used.  A
          * negative value for used signals an invalid value, which is the case the first
@@ -279,7 +296,6 @@
             }
 
             mTotalTimeUs += duration;
-            boolean reportIt = false;
 
             if (big) {
                 mBigBuilds++;
@@ -290,6 +306,9 @@
             if (mMaxBuildTimeUs < duration) {
                 mMaxBuildTimeUs = duration;
             }
+            if (mMaxUsedCount < used) {
+                mMaxUsedCount = used;
+            }
         }
 
         private Stats(long now) {
@@ -313,6 +332,7 @@
             mShortLived = orig.mShortLived;
             mTotalTimeUs = orig.mTotalTimeUs;
             mMaxBuildTimeUs = orig.mMaxBuildTimeUs;
+            mMaxUsedCount = orig.mMaxUsedCount;
         }
 
         /**
@@ -443,18 +463,19 @@
         }
 
         /**
-         * Report the object via an event.  Presumably the record indicates an anomalous
-         * incident.
+         * Report the snapshot statistics to FrameworkStatsLog.
          */
-        private void report() {
-            EventLogTags.writePmSnapshotStats(
-                    mTotalBuilds, mTotalUsed, mBigBuilds, mShortLived,
-                    mMaxBuildTimeUs / US_IN_MS, mTotalTimeUs / US_IN_MS);
+        private void logSnapshotStatistics(int packageCount) {
+            final long avgLatencyUs = (mTotalBuilds == 0 ? 0 : mTotalTimeUs / mTotalBuilds);
+            final int avgUsedCount = (mTotalBuilds == 0 ? 0 : mTotalUsed / mTotalBuilds);
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.PACKAGE_MANAGER_SNAPSHOT_REPORTED, mTimes, mUsed,
+                    mMaxBuildTimeUs, mMaxUsedCount, avgLatencyUs, avgUsedCount, packageCount);
         }
     }
 
     /**
-     * Long statistics.  These roll over approximately every week.
+     * Long statistics.  These roll over approximately one day.
      */
     private Stats[] mLong;
 
@@ -464,10 +485,14 @@
     private Stats[] mShort;
 
     /**
-     * The time of the last build.  This can be used to compute the length of time a
-     * snapshot existed before being replaced.
+     * The time of last logging to the FrameworkStatsLog.
      */
-    private long mLastBuildTime = 0;
+    private long mLastLogTimeUs;
+
+    /**
+     * The number of packages on the device.
+     */
+    private int mPackageCount;
 
     /**
      * Create a snapshot object.  Initialize the bin levels.  The last bin catches
@@ -475,8 +500,20 @@
      */
     public SnapshotStatistics() {
         // Create the bin thresholds.  The time bins are in units of us.
-        mTimeBins = new BinMap(new int[] { 1, 2, 5, 10, 20, 50, 100 });
-        mUseBins = new BinMap(new int[] { 1, 2, 5, 10, 20, 50, 100 });
+        mTimeBins = new BinMap(new int[] {
+                REBUILD_LATENCY_BUCKET_LESS_THAN_1_MILLIS,
+                REBUILD_LATENCY_BUCKET_LESS_THAN_2_MILLIS,
+                REBUILD_LATENCY_BUCKET_LESS_THAN_5_MILLIS,
+                REBUILD_LATENCY_BUCKET_LESS_THAN_10_MILLIS,
+                REBUILD_LATENCY_BUCKET_LESS_THAN_20_MILLIS,
+                REBUILD_LATENCY_BUCKET_LESS_THAN_50_MILLIS,
+                REBUILD_LATENCY_BUCKET_LESS_THAN_100_MILLIS });
+        mUseBins = new BinMap(new int[] {
+                REUSE_COUNT_BUCKET_LESS_THAN_1,
+                REUSE_COUNT_BUCKET_LESS_THAN_10,
+                REUSE_COUNT_BUCKET_LESS_THAN_100,
+                REUSE_COUNT_BUCKET_LESS_THAN_1000,
+                REUSE_COUNT_BUCKET_LESS_THAN_10000 });
 
         // Create the raw statistics
         final long now = SystemClock.currentTimeMicro();
@@ -484,6 +521,7 @@
         mLong[0] = new Stats(now);
         mShort = new Stats[10];
         mShort[0] = new Stats(now);
+        mLastLogTimeUs = now;
 
         // Create the message handler for ticks and start the ticker.
         mHandler = new Handler(Looper.getMainLooper()) {
@@ -516,13 +554,14 @@
      * @param now The time at which the snapshot rebuild began, in ns.
      * @param done The time at which the snapshot rebuild completed, in ns.
      * @param hits The number of times the previous snapshot was used.
+     * @param packageCount The number of packages on the device.
      */
-    public final void rebuild(long now, long done, int hits) {
+    public final void rebuild(long now, long done, int hits, int packageCount) {
         // The duration has a span of about 2000s
         final int duration = (int) (done - now);
         boolean reportEvent = false;
         synchronized (mLock) {
-            mLastBuildTime = now;
+            mPackageCount = packageCount;
 
             final int timeBin = mTimeBins.getBin(duration / 1000);
             final int useBin = mUseBins.getBin(hits);
@@ -570,10 +609,12 @@
     private void tick() {
         synchronized (mLock) {
             long now = SystemClock.currentTimeMicro();
-            mTicks++;
-            if (mTicks % SNAPSHOT_LONG_TICKS == 0) {
+            if (now - mLastLogTimeUs > SNAPSHOT_LOG_INTERVAL_US) {
                 shift(mLong, now);
+                mLastLogTimeUs = now;
+                mLong[mLong.length - 1].logSnapshotStatistics(mPackageCount);
             }
+
             shift(mShort, now);
             mEventsReported = 0;
         }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2bfee8c..cf0ea43 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -104,7 +104,6 @@
 import android.util.TimeUtils;
 import android.util.TypedValue;
 import android.util.Xml;
-import android.view.Display;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -625,14 +624,7 @@
     @GuardedBy("mUserStates")
     private final WatchedUserStates mUserStates = new WatchedUserStates();
 
-    /**
-     * Set on on devices that support background users (key) running on secondary displays (value).
-     */
-    // TODO(b/244644281): move such logic to a different class (like UserDisplayAssigner)
-    @Nullable
-    @GuardedBy("mUsersOnSecondaryDisplays")
-    private final SparseIntArray mUsersOnSecondaryDisplays;
-    private final boolean mUsersOnSecondaryDisplaysEnabled;
+    private final UserVisibilityMediator mUserVisibilityMediator;
 
     private static UserManagerService sInstance;
 
@@ -708,8 +700,7 @@
     @VisibleForTesting
     UserManagerService(Context context) {
         this(context, /* pm= */ null, /* userDataPreparer= */ null,
-                /* packagesLock= */ new Object(), context.getCacheDir(), /* users= */ null,
-                /* usersOnSecondaryDisplays= */ null);
+                /* packagesLock= */ new Object(), context.getCacheDir(), /* users= */ null);
     }
 
     /**
@@ -720,13 +711,13 @@
     UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer,
             Object packagesLock) {
         this(context, pm, userDataPreparer, packagesLock, Environment.getDataDirectory(),
-                /* users= */ null, /* usersOnSecondaryDisplays= */ null);
+                /* users= */ null);
     }
 
     @VisibleForTesting
     UserManagerService(Context context, PackageManagerService pm,
             UserDataPreparer userDataPreparer, Object packagesLock, File dataDir,
-            SparseArray<UserData> users, @Nullable SparseIntArray usersOnSecondaryDisplays) {
+            SparseArray<UserData> users) {
         mContext = context;
         mPm = pm;
         mPackagesLock = packagesLock;
@@ -756,14 +747,7 @@
         mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING);
         mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null;
         emulateSystemUserModeIfNeeded();
-        mUsersOnSecondaryDisplaysEnabled = UserManager.isUsersOnSecondaryDisplaysEnabled();
-        if (mUsersOnSecondaryDisplaysEnabled) {
-            mUsersOnSecondaryDisplays = usersOnSecondaryDisplays == null
-                    ? new SparseIntArray() // default behavior
-                    : usersOnSecondaryDisplays; // passed by unit test
-        } else {
-            mUsersOnSecondaryDisplays = null;
-        }
+        mUserVisibilityMediator = new UserVisibilityMediator(this);
     }
 
     void systemReady() {
@@ -1652,7 +1636,8 @@
         return isProfileUnchecked(userId);
     }
 
-    private boolean isProfileUnchecked(@UserIdInt int userId) {
+    // TODO(b/244644281): make it private once UserVisibilityMediator don't use it anymore
+    boolean isProfileUnchecked(@UserIdInt int userId) {
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
             return userInfo != null && userInfo.isProfile();
@@ -1729,11 +1714,6 @@
         return userId == getCurrentUserId();
     }
 
-    @VisibleForTesting
-    boolean isUsersOnSecondaryDisplaysEnabled() {
-        return mUsersOnSecondaryDisplaysEnabled;
-    }
-
     @Override
     public boolean isUserVisible(@UserIdInt int userId) {
         int callingUserId = UserHandle.getCallingUserId();
@@ -1744,69 +1724,7 @@
                     + ") is visible");
         }
 
-        return isUserVisibleUnchecked(userId);
-    }
-
-    @VisibleForTesting
-    boolean isUserVisibleUnchecked(@UserIdInt int userId) {
-        // First check current foreground user and their profiles (on main display)
-        if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
-            return true;
-        }
-
-        // Device doesn't support multiple users on multiple displays, so only users checked above
-        // can be visible
-        if (!mUsersOnSecondaryDisplaysEnabled) {
-            return false;
-        }
-
-        synchronized (mUsersOnSecondaryDisplays) {
-            return mUsersOnSecondaryDisplays.indexOfKey(userId) >= 0;
-        }
-    }
-
-    @VisibleForTesting
-    int getDisplayAssignedToUser(@UserIdInt int userId) {
-        if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
-            return Display.DEFAULT_DISPLAY;
-        }
-
-        if (!mUsersOnSecondaryDisplaysEnabled) {
-            return Display.INVALID_DISPLAY;
-        }
-
-        synchronized (mUsersOnSecondaryDisplays) {
-            return mUsersOnSecondaryDisplays.get(userId, Display.INVALID_DISPLAY);
-        }
-    }
-
-    @VisibleForTesting
-    int getUserAssignedToDisplay(int displayId) {
-        if (displayId == Display.DEFAULT_DISPLAY || !mUsersOnSecondaryDisplaysEnabled) {
-            return getCurrentUserId();
-        }
-
-        synchronized (mUsersOnSecondaryDisplays) {
-            for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
-                if (mUsersOnSecondaryDisplays.valueAt(i) != displayId) {
-                    continue;
-                }
-                int userId = mUsersOnSecondaryDisplays.keyAt(i);
-                if (!isProfileUnchecked(userId)) {
-                    return userId;
-                } else if (DBG_MUMD) {
-                    Slogf.d(LOG_TAG, "getUserAssignedToDisplay(%d): skipping user %d because it's "
-                            + "a profile", displayId, userId);
-                }
-            }
-        }
-
-        int currentUserId = getCurrentUserId();
-        if (DBG_MUMD) {
-            Slogf.d(LOG_TAG, "getUserAssignedToDisplay(%d): no user assigned to display, returning "
-                    + "current user (%d) instead", displayId, currentUserId);
-        }
-        return currentUserId;
+        return mUserVisibilityMediator.isUserVisible(userId);
     }
 
     /**
@@ -1850,54 +1768,9 @@
         return false;
     }
 
-    // TODO(b/239982558): try to merge with isUserVisibleUnchecked() (once both are unit tested)
-    /**
-     * See {@link UserManagerInternal#isUserVisible(int, int)}.
-     */
+    // Called by UserManagerServiceShellCommand
     boolean isUserVisibleOnDisplay(@UserIdInt int userId, int displayId) {
-        if (displayId == Display.INVALID_DISPLAY) {
-            return false;
-        }
-        if (!mUsersOnSecondaryDisplaysEnabled) {
-            return isCurrentUserOrRunningProfileOfCurrentUser(userId);
-        }
-
-        // TODO(b/244644281): temporary workaround to let WM use this API without breaking current
-        // behavior - return true for current user / profile for any display (other than those
-        // explicitly assigned to another users), otherwise they wouldn't be able to launch
-        // activities on other non-passenger displays, like cluster, display, or virtual displays).
-        // In the long-term, it should rely just on mUsersOnSecondaryDisplays, which
-        // would be updated by DisplayManagerService when displays are created / initialized.
-        if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
-            synchronized (mUsersOnSecondaryDisplays) {
-                boolean assignedToUser = false;
-                boolean assignedToAnotherUser = false;
-                for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
-                    if (mUsersOnSecondaryDisplays.valueAt(i) == displayId) {
-                        if (mUsersOnSecondaryDisplays.keyAt(i) == userId) {
-                            assignedToUser = true;
-                            break;
-                        } else {
-                            assignedToAnotherUser = true;
-                            // Cannot break because it could be assigned to a profile of the user
-                            // (and we better not assume that the iteration will check for the
-                            // parent user before its profiles)
-                        }
-                    }
-                }
-                if (DBG_MUMD) {
-                    Slogf.d(LOG_TAG, "isUserVisibleOnDisplay(%d, %d): assignedToUser=%b, "
-                            + "assignedToAnotherUser=%b, mUsersOnSecondaryDisplays=%s",
-                            userId, displayId, assignedToUser, assignedToAnotherUser,
-                            mUsersOnSecondaryDisplays);
-                }
-                return assignedToUser || !assignedToAnotherUser;
-            }
-        }
-
-        synchronized (mUsersOnSecondaryDisplays) {
-            return mUsersOnSecondaryDisplays.get(userId, Display.INVALID_DISPLAY) == displayId;
-        }
+        return mUserVisibilityMediator.isUserVisible(userId, displayId);
     }
 
     @Override
@@ -1915,7 +1788,7 @@
                 for (int i = 0; i < usersSize; i++) {
                     UserInfo ui = mUsers.valueAt(i).info;
                     if (!ui.partial && !ui.preCreated && !mRemovingUserIds.get(ui.id)
-                            && isUserVisibleUnchecked(ui.id)) {
+                            && mUserVisibilityMediator.isUserVisible(ui.id)) {
                         visibleUsers.add(UserHandle.of(ui.id));
                     }
                 }
@@ -6251,9 +6124,15 @@
         final long nowRealtime = SystemClock.elapsedRealtime();
         final StringBuilder sb = new StringBuilder();
 
-        if (args != null && args.length > 0 && args[0].equals("--user")) {
-            dumpUser(pw, UserHandle.parseUserArg(args[1]), sb, now, nowRealtime);
-            return;
+        if (args != null && args.length > 0) {
+            switch (args[0]) {
+                case "--user":
+                    dumpUser(pw, UserHandle.parseUserArg(args[1]), sb, now, nowRealtime);
+                    return;
+                case "--visibility-mediator":
+                    mUserVisibilityMediator.dump(pw);
+                    return;
+            }
         }
 
         final int currentUserId = getCurrentUserId();
@@ -6316,18 +6195,9 @@
             }
         } // synchronized (mPackagesLock)
 
-        // Multiple Users on Multiple Display info
-        pw.println("  Supports users on secondary displays: " + mUsersOnSecondaryDisplaysEnabled);
-        // mUsersOnSecondaryDisplaysEnabled is set on constructor, while the UserManager API is
-        // set dynamically, so print both to help cases where the developer changed it on the fly
-        pw.println("  UM.isUsersOnSecondaryDisplaysEnabled(): "
-                + UserManager.isUsersOnSecondaryDisplaysEnabled());
-        if (mUsersOnSecondaryDisplaysEnabled) {
-            pw.print("  Users on secondary displays: ");
-            synchronized (mUsersOnSecondaryDisplays) {
-                pw.println(mUsersOnSecondaryDisplays);
-            }
-        }
+        pw.println();
+        mUserVisibilityMediator.dump(pw);
+        pw.println();
 
         // Dump some capabilities
         pw.println();
@@ -6899,133 +6769,33 @@
         }
 
         @Override
-        public void assignUserToDisplay(int userId, int displayId) {
-            if (DBG_MUMD) {
-                Slogf.d(LOG_TAG, "assignUserToDisplay(%d, %d)", userId, displayId);
-            }
-
-            // NOTE: Using Boolean instead of boolean as it will be re-used below
-            Boolean isProfile = null;
-            if (displayId == Display.DEFAULT_DISPLAY) {
-                if (mUsersOnSecondaryDisplaysEnabled) {
-                    // Profiles are only supported in the default display, but it cannot return yet
-                    // as it needs to check if the parent is also assigned to the DEFAULT_DISPLAY
-                    // (this is done indirectly below when it checks that the profile parent is the
-                    // current user, as the current user is always assigned to the DEFAULT_DISPLAY).
-                    isProfile = isProfileUnchecked(userId);
-                }
-                if (isProfile == null || !isProfile) {
-                    // Don't need to do anything because methods (such as isUserVisible()) already
-                    // know that the current user (and their profiles) is assigned to the default
-                    // display.
-                    if (DBG_MUMD) {
-                        Slogf.d(LOG_TAG, "ignoring on default display");
-                    }
-                    return;
-                }
-            }
-
-            if (!mUsersOnSecondaryDisplaysEnabled) {
-                throw new UnsupportedOperationException("assignUserToDisplay(" + userId + ", "
-                        + displayId + ") called on device that doesn't support multiple "
-                        + "users on multiple displays");
-            }
-
-            Preconditions.checkArgument(userId != UserHandle.USER_SYSTEM, "Cannot assign system "
-                    + "user to secondary display (%d)", displayId);
-            Preconditions.checkArgument(displayId != Display.INVALID_DISPLAY,
-                    "Cannot assign to INVALID_DISPLAY (%d)", displayId);
-
-            int currentUserId = getCurrentUserId();
-            Preconditions.checkArgument(userId != currentUserId,
-                    "Cannot assign current user (%d) to other displays", currentUserId);
-
-            if (isProfile == null) {
-                isProfile = isProfileUnchecked(userId);
-            }
-            synchronized (mUsersOnSecondaryDisplays) {
-                if (isProfile) {
-                    // Profile can only start in the same display as parent. And for simplicity,
-                    // that display must be the DEFAULT_DISPLAY.
-                    Preconditions.checkArgument(displayId == Display.DEFAULT_DISPLAY,
-                            "Profile user can only be started in the default display");
-                    int parentUserId = getProfileParentId(userId);
-                    Preconditions.checkArgument(parentUserId == currentUserId,
-                            "Only profile of current user can be assigned to a display");
-                    if (DBG_MUMD) {
-                        Slogf.d(LOG_TAG, "Ignoring profile user %d on default display", userId);
-                    }
-                    return;
-                }
-
-                // Check if display is available
-                for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
-                    int assignedUserId = mUsersOnSecondaryDisplays.keyAt(i);
-                    int assignedDisplayId = mUsersOnSecondaryDisplays.valueAt(i);
-                    if (DBG_MUMD) {
-                        Slogf.d(LOG_TAG, "%d: assignedUserId=%d, assignedDisplayId=%d",
-                                i, assignedUserId, assignedDisplayId);
-                    }
-                    if (displayId == assignedDisplayId) {
-                        throw new IllegalStateException("Cannot assign user " + userId + " to "
-                                + "display " + displayId + " because such display is already "
-                                + "assigned to user " + assignedUserId);
-                    }
-                    if (userId == assignedUserId) {
-                        throw new IllegalStateException("Cannot assign user " + userId + " to "
-                                + "display " + displayId + " because such user is as already "
-                                + "assigned to display " + assignedDisplayId);
-                    }
-                }
-
-                if (DBG_MUMD) {
-                    Slogf.d(LOG_TAG, "Adding full user %d -> display %d", userId, displayId);
-                }
-                mUsersOnSecondaryDisplays.put(userId, displayId);
-            }
+        public void assignUserToDisplay(@UserIdInt int userId, int displayId) {
+            mUserVisibilityMediator.assignUserToDisplay(userId, displayId);
         }
 
         @Override
         public void unassignUserFromDisplay(@UserIdInt int userId) {
-            if (DBG_MUMD) {
-                Slogf.d(LOG_TAG, "unassignUserFromDisplay(%d)", userId);
-            }
-            if (!mUsersOnSecondaryDisplaysEnabled) {
-                // Don't need to do anything because methods (such as isUserVisible()) already know
-                // that the current user (and their profiles) is assigned to the default display.
-                if (DBG_MUMD) {
-                    Slogf.d(LOG_TAG, "ignoring when device doesn't support MUMD");
-                }
-                return;
-            }
-
-            synchronized (mUsersOnSecondaryDisplays) {
-                if (DBG_MUMD) {
-                    Slogf.d(LOG_TAG, "Removing %d from mUsersOnSecondaryDisplays (%s)", userId,
-                            mUsersOnSecondaryDisplays);
-                }
-                mUsersOnSecondaryDisplays.delete(userId);
-            }
+            mUserVisibilityMediator.unassignUserFromDisplay(userId);
         }
 
         @Override
-        public boolean isUserVisible(int userId) {
-            return isUserVisibleUnchecked(userId);
+        public boolean isUserVisible(@UserIdInt int userId) {
+            return mUserVisibilityMediator.isUserVisible(userId);
         }
 
         @Override
-        public boolean isUserVisible(int userId, int displayId) {
-            return isUserVisibleOnDisplay(userId, displayId);
+        public boolean isUserVisible(@UserIdInt int userId, int displayId) {
+            return mUserVisibilityMediator.isUserVisible(userId, displayId);
         }
 
         @Override
-        public int getDisplayAssignedToUser(int userId) {
-            return UserManagerService.this.getDisplayAssignedToUser(userId);
+        public int getDisplayAssignedToUser(@UserIdInt int userId) {
+            return mUserVisibilityMediator.getDisplayAssignedToUser(userId);
         }
 
         @Override
-        public int getUserAssignedToDisplay(int displayId) {
-            return UserManagerService.this.getUserAssignedToDisplay(displayId);
+        public @UserIdInt int getUserAssignedToDisplay(int displayId) {
+            return mUserVisibilityMediator.getUserAssignedToDisplay(displayId);
         }
     } // class LocalService
 
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index d1f3341e..dbd026e 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -147,7 +147,8 @@
             UserManager.DISALLOW_WIFI_TETHERING,
             UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI,
             UserManager.DISALLOW_WIFI_DIRECT,
-            UserManager.DISALLOW_ADD_WIFI_CONFIG
+            UserManager.DISALLOW_ADD_WIFI_CONFIG,
+            UserManager.DISALLOW_CELLULAR_2G
     });
 
     public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
@@ -195,7 +196,8 @@
             UserManager.DISALLOW_CHANGE_WIFI_STATE,
             UserManager.DISALLOW_WIFI_TETHERING,
             UserManager.DISALLOW_WIFI_DIRECT,
-            UserManager.DISALLOW_ADD_WIFI_CONFIG
+            UserManager.DISALLOW_ADD_WIFI_CONFIG,
+            UserManager.DISALLOW_CELLULAR_2G
     );
 
     /**
@@ -234,7 +236,8 @@
                     UserManager.DISALLOW_CHANGE_WIFI_STATE,
                     UserManager.DISALLOW_WIFI_TETHERING,
                     UserManager.DISALLOW_WIFI_DIRECT,
-                    UserManager.DISALLOW_ADD_WIFI_CONFIG
+                    UserManager.DISALLOW_ADD_WIFI_CONFIG,
+                    UserManager.DISALLOW_CELLULAR_2G
     );
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
new file mode 100644
index 0000000..f725c48
--- /dev/null
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -0,0 +1,350 @@
+/*
+ * 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.pm;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.IndentingPrintWriter;
+import android.util.SparseIntArray;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.utils.Slogf;
+
+import java.io.PrintWriter;
+
+/**
+ * Class responsible for deciding whether a user is visible (or visible for a given display).
+ *
+ * <p>This class is thread safe.
+ */
+// TODO(b/244644281): improve javadoc (for example, explain all cases / modes)
+public final class UserVisibilityMediator {
+
+    private static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
+
+    private static final String TAG = UserVisibilityMediator.class.getSimpleName();
+
+    private final Object mLock = new Object();
+
+    // TODO(b/244644281): should not depend on service, but keep its own internal state (like
+    // current user and profile groups), but it is initially as the code was just moved from UMS
+    // "as is". Similarly, it shouldn't need to pass the SparseIntArray on constructor (which was
+    // added to UMS for testing purposes)
+    private final UserManagerService mService;
+
+    private final boolean mUsersOnSecondaryDisplaysEnabled;
+
+    @Nullable
+    @GuardedBy("mLock")
+    private final SparseIntArray mUsersOnSecondaryDisplays;
+
+    UserVisibilityMediator(UserManagerService service) {
+        this(service, UserManager.isUsersOnSecondaryDisplaysEnabled(),
+                /* usersOnSecondaryDisplays= */ null);
+    }
+
+    @VisibleForTesting
+    UserVisibilityMediator(UserManagerService service, boolean usersOnSecondaryDisplaysEnabled,
+            @Nullable SparseIntArray usersOnSecondaryDisplays) {
+        mService = service;
+        mUsersOnSecondaryDisplaysEnabled = usersOnSecondaryDisplaysEnabled;
+        if (mUsersOnSecondaryDisplaysEnabled) {
+            mUsersOnSecondaryDisplays = usersOnSecondaryDisplays == null
+                    ? new SparseIntArray() // default behavior
+                    : usersOnSecondaryDisplays; // passed by unit test
+        } else {
+            mUsersOnSecondaryDisplays = null;
+        }
+    }
+
+    /**
+     * See {@link UserManagerInternal#assignUserToDisplay(int, int)}.
+     */
+    public void assignUserToDisplay(int userId, int displayId) {
+        if (DBG) {
+            Slogf.d(TAG, "assignUserToDisplay(%d, %d)", userId, displayId);
+        }
+
+        // NOTE: Using Boolean instead of boolean as it will be re-used below
+        Boolean isProfile = null;
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            if (mUsersOnSecondaryDisplaysEnabled) {
+                // Profiles are only supported in the default display, but it cannot return yet
+                // as it needs to check if the parent is also assigned to the DEFAULT_DISPLAY
+                // (this is done indirectly below when it checks that the profile parent is the
+                // current user, as the current user is always assigned to the DEFAULT_DISPLAY).
+                isProfile = isProfileUnchecked(userId);
+            }
+            if (isProfile == null || !isProfile) {
+                // Don't need to do anything because methods (such as isUserVisible()) already
+                // know that the current user (and their profiles) is assigned to the default
+                // display.
+                if (DBG) {
+                    Slogf.d(TAG, "ignoring on default display");
+                }
+                return;
+            }
+        }
+
+        if (!mUsersOnSecondaryDisplaysEnabled) {
+            throw new UnsupportedOperationException("assignUserToDisplay(" + userId + ", "
+                    + displayId + ") called on device that doesn't support multiple "
+                    + "users on multiple displays");
+        }
+
+        Preconditions.checkArgument(userId != UserHandle.USER_SYSTEM, "Cannot assign system "
+                + "user to secondary display (%d)", displayId);
+        Preconditions.checkArgument(displayId != Display.INVALID_DISPLAY,
+                "Cannot assign to INVALID_DISPLAY (%d)", displayId);
+
+        int currentUserId = getCurrentUserId();
+        Preconditions.checkArgument(userId != currentUserId,
+                "Cannot assign current user (%d) to other displays", currentUserId);
+
+        if (isProfile == null) {
+            isProfile = isProfileUnchecked(userId);
+        }
+        synchronized (mLock) {
+            if (isProfile) {
+                // Profile can only start in the same display as parent. And for simplicity,
+                // that display must be the DEFAULT_DISPLAY.
+                Preconditions.checkArgument(displayId == Display.DEFAULT_DISPLAY,
+                        "Profile user can only be started in the default display");
+                int parentUserId = getProfileParentId(userId);
+                Preconditions.checkArgument(parentUserId == currentUserId,
+                        "Only profile of current user can be assigned to a display");
+                if (DBG) {
+                    Slogf.d(TAG, "Ignoring profile user %d on default display", userId);
+                }
+                return;
+            }
+
+            // Check if display is available
+            for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
+                int assignedUserId = mUsersOnSecondaryDisplays.keyAt(i);
+                int assignedDisplayId = mUsersOnSecondaryDisplays.valueAt(i);
+                if (DBG) {
+                    Slogf.d(TAG, "%d: assignedUserId=%d, assignedDisplayId=%d",
+                            i, assignedUserId, assignedDisplayId);
+                }
+                if (displayId == assignedDisplayId) {
+                    throw new IllegalStateException("Cannot assign user " + userId + " to "
+                            + "display " + displayId + " because such display is already "
+                            + "assigned to user " + assignedUserId);
+                }
+                if (userId == assignedUserId) {
+                    throw new IllegalStateException("Cannot assign user " + userId + " to "
+                            + "display " + displayId + " because such user is as already "
+                            + "assigned to display " + assignedDisplayId);
+                }
+            }
+
+            if (DBG) {
+                Slogf.d(TAG, "Adding full user %d -> display %d", userId, displayId);
+            }
+            mUsersOnSecondaryDisplays.put(userId, displayId);
+        }
+    }
+
+    /**
+     * See {@link UserManagerInternal#unassignUserFromDisplay(int)}.
+     */
+    public void unassignUserFromDisplay(int userId) {
+        if (DBG) {
+            Slogf.d(TAG, "unassignUserFromDisplay(%d)", userId);
+        }
+        if (!mUsersOnSecondaryDisplaysEnabled) {
+            // Don't need to do anything because methods (such as isUserVisible()) already know
+            // that the current user (and their profiles) is assigned to the default display.
+            if (DBG) {
+                Slogf.d(TAG, "ignoring when device doesn't support MUMD");
+            }
+            return;
+        }
+
+        synchronized (mLock) {
+            if (DBG) {
+                Slogf.d(TAG, "Removing %d from mUsersOnSecondaryDisplays (%s)", userId,
+                        mUsersOnSecondaryDisplays);
+            }
+            mUsersOnSecondaryDisplays.delete(userId);
+        }
+    }
+
+    /**
+     * See {@link UserManagerInternal#isUserVisible(int)}.
+     */
+    public boolean isUserVisible(int userId) {
+        // First check current foreground user and their profiles (on main display)
+        if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
+            return true;
+        }
+
+        // Device doesn't support multiple users on multiple displays, so only users checked above
+        // can be visible
+        if (!mUsersOnSecondaryDisplaysEnabled) {
+            return false;
+        }
+
+        synchronized (mLock) {
+            return mUsersOnSecondaryDisplays.indexOfKey(userId) >= 0;
+        }
+    }
+
+    /**
+     * See {@link UserManagerInternal#isUserVisible(int, int)}.
+     */
+    public boolean isUserVisible(int userId, int displayId) {
+        if (displayId == Display.INVALID_DISPLAY) {
+            return false;
+        }
+        if (!mUsersOnSecondaryDisplaysEnabled) {
+            return isCurrentUserOrRunningProfileOfCurrentUser(userId);
+        }
+
+        // TODO(b/244644281): temporary workaround to let WM use this API without breaking current
+        // behavior - return true for current user / profile for any display (other than those
+        // explicitly assigned to another users), otherwise they wouldn't be able to launch
+        // activities on other non-passenger displays, like cluster, display, or virtual displays).
+        // In the long-term, it should rely just on mUsersOnSecondaryDisplays, which
+        // would be updated by DisplayManagerService when displays are created / initialized.
+        if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
+            synchronized (mLock) {
+                boolean assignedToUser = false;
+                boolean assignedToAnotherUser = false;
+                for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
+                    if (mUsersOnSecondaryDisplays.valueAt(i) == displayId) {
+                        if (mUsersOnSecondaryDisplays.keyAt(i) == userId) {
+                            assignedToUser = true;
+                            break;
+                        } else {
+                            assignedToAnotherUser = true;
+                            // Cannot break because it could be assigned to a profile of the user
+                            // (and we better not assume that the iteration will check for the
+                            // parent user before its profiles)
+                        }
+                    }
+                }
+                if (DBG) {
+                    Slogf.d(TAG, "isUserVisibleOnDisplay(%d, %d): assignedToUser=%b, "
+                            + "assignedToAnotherUser=%b, mUsersOnSecondaryDisplays=%s",
+                            userId, displayId, assignedToUser, assignedToAnotherUser,
+                            mUsersOnSecondaryDisplays);
+                }
+                return assignedToUser || !assignedToAnotherUser;
+            }
+        }
+
+        synchronized (mLock) {
+            return mUsersOnSecondaryDisplays.get(userId, Display.INVALID_DISPLAY) == displayId;
+        }
+    }
+
+    /**
+     * See {@link UserManagerInternal#getDisplayAssignedToUser(int)}.
+     */
+    public int getDisplayAssignedToUser(int userId) {
+        if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
+            return Display.DEFAULT_DISPLAY;
+        }
+
+        if (!mUsersOnSecondaryDisplaysEnabled) {
+            return Display.INVALID_DISPLAY;
+        }
+
+        synchronized (mLock) {
+            return mUsersOnSecondaryDisplays.get(userId, Display.INVALID_DISPLAY);
+        }
+    }
+
+    /**
+     * See {@link UserManagerInternal#getUserAssignedToDisplay(int)}.
+     */
+    public int getUserAssignedToDisplay(int displayId) {
+        if (displayId == Display.DEFAULT_DISPLAY || !mUsersOnSecondaryDisplaysEnabled) {
+            return getCurrentUserId();
+        }
+
+        synchronized (mLock) {
+            for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
+                if (mUsersOnSecondaryDisplays.valueAt(i) != displayId) {
+                    continue;
+                }
+                int userId = mUsersOnSecondaryDisplays.keyAt(i);
+                if (!isProfileUnchecked(userId)) {
+                    return userId;
+                } else if (DBG) {
+                    Slogf.d(TAG, "getUserAssignedToDisplay(%d): skipping user %d because it's "
+                            + "a profile", displayId, userId);
+                }
+            }
+        }
+
+        int currentUserId = getCurrentUserId();
+        if (DBG) {
+            Slogf.d(TAG, "getUserAssignedToDisplay(%d): no user assigned to display, returning "
+                    + "current user (%d) instead", displayId, currentUserId);
+        }
+        return currentUserId;
+    }
+
+    private void dump(IndentingPrintWriter ipw) {
+        ipw.println("UserVisibilityManager");
+        ipw.increaseIndent();
+
+        ipw.print("Supports users on secondary displays: ");
+        ipw.println(mUsersOnSecondaryDisplaysEnabled);
+
+        if (mUsersOnSecondaryDisplaysEnabled) {
+            ipw.print("Users on secondary displays: ");
+            synchronized (mLock) {
+                ipw.println(mUsersOnSecondaryDisplays);
+            }
+        }
+
+        ipw.decreaseIndent();
+    }
+
+    void dump(PrintWriter pw) {
+        if (pw instanceof IndentingPrintWriter) {
+            dump((IndentingPrintWriter) pw);
+            return;
+        }
+        dump(new IndentingPrintWriter(pw));
+    }
+
+    // TODO(b/244644281): remove methods below once this class caches that state
+    private @UserIdInt int getCurrentUserId() {
+        return mService.getCurrentUserId();
+    }
+
+    private boolean isCurrentUserOrRunningProfileOfCurrentUser(@UserIdInt int userId) {
+        return mService.isCurrentUserOrRunningProfileOfCurrentUser(userId);
+    }
+
+    private boolean isProfileUnchecked(@UserIdInt int userId) {
+        return mService.isProfileUnchecked(userId);
+    }
+
+    private @UserIdInt int getProfileParentId(@UserIdInt int userId) {
+        return mService.getProfileParentId(userId);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 799ef41..ab223ef 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -4245,7 +4245,6 @@
         }
         boolean changed = false;
 
-        Set<Permission> needsUpdate = null;
         synchronized (mLock) {
             final Iterator<Permission> it = mRegistry.getPermissionTrees().iterator();
             while (it.hasNext()) {
@@ -4264,26 +4263,6 @@
                             + " that used to be declared by " + bp.getPackageName());
                     it.remove();
                 }
-                if (needsUpdate == null) {
-                    needsUpdate = new ArraySet<>();
-                }
-                needsUpdate.add(bp);
-            }
-        }
-        if (needsUpdate != null) {
-            for (final Permission bp : needsUpdate) {
-                final AndroidPackage sourcePkg =
-                        mPackageManagerInt.getPackage(bp.getPackageName());
-                final PackageStateInternal sourcePs =
-                        mPackageManagerInt.getPackageStateInternal(bp.getPackageName());
-                synchronized (mLock) {
-                    if (sourcePkg != null && sourcePs != null) {
-                        continue;
-                    }
-                    Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
-                            + " from package " + bp.getPackageName());
-                    mRegistry.removePermission(bp.getName());
-                }
             }
         }
         return changed;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c4e122d..98b5c1b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -256,6 +256,7 @@
     static final int SHORT_PRESS_POWER_GO_HOME = 4;
     static final int SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME = 5;
     static final int SHORT_PRESS_POWER_LOCK_OR_SLEEP = 6;
+    static final int SHORT_PRESS_POWER_DREAM_OR_SLEEP = 7;
 
     // must match: config_LongPressOnPowerBehavior in config.xml
     static final int LONG_PRESS_POWER_NOTHING = 0;
@@ -969,7 +970,14 @@
             powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
         } else if (count > 3 && count <= getMaxMultiPressPowerCount()) {
             Slog.d(TAG, "No behavior defined for power press count " + count);
-        } else if (count == 1 && interactive && !beganFromNonInteractive) {
+        } else if (count == 1 && interactive) {
+            if (beganFromNonInteractive) {
+                // The "screen is off" case, where we might want to start dreaming on power button
+                // press.
+                attemptToDreamFromShortPowerButtonPress(false, () -> {});
+                return;
+            }
+
             if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) {
                 Slog.i(TAG, "Suppressing power key because the user is interacting with the "
                         + "fingerprint sensor");
@@ -1018,11 +1026,39 @@
                     }
                     break;
                 }
+                case SHORT_PRESS_POWER_DREAM_OR_SLEEP: {
+                    attemptToDreamFromShortPowerButtonPress(
+                            true,
+                            () -> sleepDefaultDisplayFromPowerButton(eventTime, 0));
+                    break;
+                }
             }
         }
     }
 
     /**
+     * Attempt to dream from a power button press.
+     *
+     * @param isScreenOn Whether the screen is currently on.
+     * @param noDreamAction The action to perform if dreaming is not possible.
+     */
+    private void attemptToDreamFromShortPowerButtonPress(
+            boolean isScreenOn, Runnable noDreamAction) {
+        if (mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_SLEEP) {
+            noDreamAction.run();
+            return;
+        }
+
+        final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
+        if (dreamManagerInternal == null || !dreamManagerInternal.canStartDreaming(isScreenOn)) {
+            noDreamAction.run();
+            return;
+        }
+
+        dreamManagerInternal.requestDream();
+    }
+
+    /**
      * Sends the default display to sleep as a result of a power button press.
      *
      * @return {@code true} if the device was sent to sleep, {@code false} if the device did not
@@ -1593,7 +1629,8 @@
 
         // If there's a dream running then use home to escape the dream
         // but don't actually go home.
-        if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) {
+        final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
+        if (dreamManagerInternal != null && dreamManagerInternal.isDreaming()) {
             mDreamManagerInternal.stopDream(false /*immediate*/, "short press on home" /*reason*/);
             return;
         }
@@ -2529,6 +2566,15 @@
         }
     }
 
+    private DreamManagerInternal getDreamManagerInternal() {
+        if (mDreamManagerInternal == null) {
+            // If mDreamManagerInternal is null, attempt to re-fetch it.
+            mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
+        }
+
+        return mDreamManagerInternal;
+    }
+
     private void updateWakeGestureListenerLp() {
         if (shouldEnableWakeGestureLp()) {
             mWakeGestureListener.requestWakeUpTrigger();
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
index 5f76fbc..79e35c2 100644
--- a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
@@ -384,7 +384,7 @@
     private static final class Wakeup {
         private static final String PARSER_TAG = "CpuWakeupStats.Wakeup";
         private static final String ABORT_REASON_PREFIX = "Abort";
-        private static final Pattern sIrqPattern = Pattern.compile("(\\d+)\\s+(\\S+)");
+        private static final Pattern sIrqPattern = Pattern.compile("^(\\d+)\\s+(\\S+)");
 
         String mRawReason;
         long mElapsedMillis;
@@ -409,7 +409,7 @@
             IrqDevice[] parsedDevices = new IrqDevice[components.length];
 
             for (String component : components) {
-                final Matcher matcher = sIrqPattern.matcher(component);
+                final Matcher matcher = sIrqPattern.matcher(component.trim());
                 if (matcher.find()) {
                     final int line;
                     final String device;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index d378b11..c59b90f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -16,12 +16,15 @@
 
 package com.android.server.statusbar;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
 import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
 import static android.app.StatusBarManager.NAV_BAR_MODE_DEFAULT;
 import static android.app.StatusBarManager.NAV_BAR_MODE_KIDS;
 import static android.app.StatusBarManager.NavBarMode;
 import static android.app.StatusBarManager.SessionFlags;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
 
@@ -1304,18 +1307,23 @@
                 "StatusBarManagerService");
     }
 
+    private boolean doesCallerHoldInteractAcrossUserPermission() {
+        return mContext.checkCallingPermission(INTERACT_ACROSS_USERS_FULL) == PERMISSION_GRANTED
+                || mContext.checkCallingPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED;
+    }
+
     /**
      *  For targetSdk S+ we require STATUS_BAR. For targetSdk < S, we only require EXPAND_STATUS_BAR
      *  but also require that it falls into one of the allowed use-cases to lock down abuse vector.
      */
     private boolean checkCanCollapseStatusBar(String method) {
         int uid = Binder.getCallingUid();
-        int pid = Binder.getCallingUid();
+        int pid = Binder.getCallingPid();
         if (CompatChanges.isChangeEnabled(LOCK_DOWN_COLLAPSE_STATUS_BAR, uid)) {
             enforceStatusBar();
         } else {
             if (mContext.checkPermission(Manifest.permission.STATUS_BAR, pid, uid)
-                    != PackageManager.PERMISSION_GRANTED) {
+                    != PERMISSION_GRANTED) {
                 enforceExpandStatusBar();
                 if (!mActivityTaskManager.canCloseSystemDialogs(pid, uid)) {
                     Slog.e(TAG, "Permission Denial: Method " + method + "() requires permission "
@@ -2021,6 +2029,11 @@
         }
 
         final int userId = mCurrentUserId;
+        final int callingUserId = UserHandle.getUserId(Binder.getCallingUid());
+        if (mCurrentUserId != callingUserId && !doesCallerHoldInteractAcrossUserPermission()) {
+            throw new SecurityException("Calling user id: " + callingUserId
+                    + ", cannot call on behalf of current user id: " + mCurrentUserId + ".");
+        }
         final long userIdentity = Binder.clearCallingIdentity();
         try {
             Settings.Secure.putIntForUser(mContext.getContentResolver(),
diff --git a/services/core/java/com/android/server/utils/EventLogger.java b/services/core/java/com/android/server/utils/EventLogger.java
index 004312f..11766a3 100644
--- a/services/core/java/com/android/server/utils/EventLogger.java
+++ b/services/core/java/com/android/server/utils/EventLogger.java
@@ -43,7 +43,7 @@
     /**
      * The maximum number of events to keep in {@code mEvents}.
      *
-     * <p>Calling {@link #log} when the size of {@link #mEvents} matches the threshold will
+     * <p>Calling {@link #enqueue} when the size of {@link #mEvents} matches the threshold will
      * cause the oldest event to be evicted.
      */
     private final int mMemSize;
@@ -60,7 +60,7 @@
     }
 
     /** Enqueues {@code event} to be logged. */
-    public synchronized void log(Event event) {
+    public synchronized void enqueue(Event event) {
         if (mEvents.size() >= mMemSize) {
             mEvents.removeLast();
         }
@@ -69,24 +69,14 @@
     }
 
     /**
-     * Add a string-based event to the log, and print it to logcat as info.
-     * @param msg the message for the logs
-     * @param tag the logcat tag to use
-     */
-    public synchronized void loglogi(String msg, String tag) {
-        final Event event = new StringEvent(msg);
-        log(event.printLog(tag));
-    }
-
-    /**
-     * Same as {@link #loglogi(String, String)} but specifying the logcat type
+     * Add a string-based event to the log, and print it to logcat with a specific severity.
      * @param msg the message for the logs
      * @param logType the type of logcat entry
      * @param tag the logcat tag to use
      */
-    public synchronized void loglog(String msg, @Event.LogType int logType, String tag) {
+    public synchronized void enqueueAndLog(String msg, @Event.LogType int logType, String tag) {
         final Event event = new StringEvent(msg);
-        log(event.printLog(logType, tag));
+        enqueue(event.printLog(logType, tag));
     }
 
     /** Dumps events using {@link PrintWriter}. */
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index abb57bc..6a35533 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -320,9 +320,7 @@
                                 IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
                                     @Override
                                     public void sendResult(Bundle data) throws RemoteException {
-                                        if (DEBUG) {
-                                            Slog.d(TAG, "publish system wallpaper changed!");
-                                        }
+                                        Slog.d(TAG, "publish system wallpaper changed!");
                                         notifyWallpaperChanged(wallpaper);
                                     }
                                 };
@@ -1552,6 +1550,7 @@
                         mReply.sendResult(null);
                     } catch (RemoteException e) {
                         Binder.restoreCallingIdentity(ident);
+                        Slog.d(TAG, "failed to send callback!", e);
                     }
                     mReply = null;
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 7d84bdf..d7c5e93 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -424,7 +424,7 @@
         try {
             harmfulAppWarning = mService.getPackageManager()
                     .getHarmfulAppWarning(mAInfo.packageName, mUserId);
-        } catch (RemoteException ex) {
+        } catch (RemoteException | IllegalArgumentException ex) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ecc43f7..b153a85 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1316,7 +1316,7 @@
                 mAppSwitchesState = APP_SWITCH_ALLOW;
             }
         }
-        return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null,
+        return pir.sendInner(caller, 0, fillInIntent, resolvedType, allowlistToken, null, null,
                 resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
     }
 
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index b9fa80c..7e56dbf 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -26,7 +26,6 @@
 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
-import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
@@ -289,9 +288,8 @@
         final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
         // Always use windowing mode fullscreen when get insets for window metrics to make sure it
         // contains all insets types.
-        final InsetsState originalState = mDisplayContent.getInsetsPolicy()
-                .enforceInsetsPolicyForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop,
-                        attrs.type, mStateController.getRawInsetsState());
+        final InsetsState originalState = enforceInsetsPolicyForTarget(WINDOWING_MODE_FULLSCREEN,
+                alwaysOnTop, attrs, mStateController.getRawInsetsState());
         InsetsState state = adjustVisibilityForTransientTypes(originalState);
         return adjustInsetsForRoundedCorners(token, state, state == originalState);
     }
@@ -343,56 +341,42 @@
 
 
     /**
-     * Modifies the given {@code state} according to the {@code type} (Inset type) provided by
-     * the target.
-     * When performing layout of the target or dispatching insets to the target, we need to exclude
-     * sources which should not be visible to the target. e.g., the source which represents the
-     * target window itself, and the IME source when the target is above IME. We also need to
-     * exclude certain types of insets source for client within specific windowing modes.
+     * Modifies the given {@code state} according to the target's window state.
+     * When performing layout of the target or dispatching insets to the target, we need to adjust
+     * sources based on the target. e.g., the floating window will not receive system bars other
+     * than caption, and some insets provider may request to override sizes for given window types.
+     * Since the window type and the insets types provided by the window shall not change at
+     * runtime, rotation doesn't matter in the layout params.
      *
-     * @param type the inset type provided by the target
      * @param windowingMode the windowing mode of the target
      * @param isAlwaysOnTop is the target always on top
-     * @param windowType the type of the target
+     * @param attrs the layout params of the target
      * @param state the input inset state containing all the sources
      * @return The state stripped of the necessary information.
      */
-    InsetsState enforceInsetsPolicyForTarget(@InternalInsetsType int type,
-            @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop,
-            int windowType, InsetsState state) {
+    InsetsState enforceInsetsPolicyForTarget(@WindowConfiguration.WindowingMode int windowingMode,
+            boolean isAlwaysOnTop, WindowManager.LayoutParams attrs, InsetsState state) {
         boolean stateCopied = false;
 
-        if (type != ITYPE_INVALID) {
+        if (attrs.providedInsets != null && attrs.providedInsets.length > 0) {
             state = new InsetsState(state);
             stateCopied = true;
-            state.removeSource(type);
-
-            // Navigation bar doesn't get influenced by anything else
-            if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
-                state.removeSource(ITYPE_STATUS_BAR);
-                state.removeSource(ITYPE_CLIMATE_BAR);
-                state.removeSource(ITYPE_CAPTION_BAR);
-                state.removeSource(ITYPE_NAVIGATION_BAR);
-                state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
-            }
-
-            // Status bar doesn't get influenced by caption bar
-            if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
-                state.removeSource(ITYPE_CAPTION_BAR);
+            for (int i = attrs.providedInsets.length - 1; i >= 0; i--) {
+                state.removeSource(attrs.providedInsets[i].type);
             }
         }
         ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController
                 .getSourceProviders();
         for (int i = providers.size() - 1; i >= 0; i--) {
             WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i);
-            if (otherProvider.overridesFrame(windowType)) {
+            if (otherProvider.overridesFrame(attrs.type)) {
                 if (!stateCopied) {
                     state = new InsetsState(state);
                     stateCopied = true;
                 }
                 InsetsSource override =
                         new InsetsSource(state.getSource(otherProvider.getSource().getType()));
-                override.setFrame(otherProvider.getOverriddenFrame(windowType));
+                override.setFrame(otherProvider.getOverriddenFrame(attrs.type));
                 state.addSource(override);
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6d750a4..f30c435 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -29,7 +29,6 @@
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.InsetsState.ITYPE_IME;
-import static android.view.InsetsState.ITYPE_INVALID;
 import static android.view.SurfaceControl.Transaction;
 import static android.view.SurfaceControl.getGlobalTransaction;
 import static android.view.ViewRootImpl.LOCAL_LAYOUT;
@@ -1673,14 +1672,11 @@
         if (rotatedState != null) {
             return insetsPolicy.adjustInsetsForWindow(this, rotatedState);
         }
-        final InsetsSourceProvider provider = getControllableInsetProvider();
-        final @InternalInsetsType int insetTypeProvidedByWindow = provider != null
-                ? provider.getSource().getType() : ITYPE_INVALID;
         final InsetsState rawInsetsState =
                 mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState();
         final InsetsState insetsStateForWindow = insetsPolicy
-                .enforceInsetsPolicyForTarget(insetTypeProvidedByWindow,
-                        getWindowingMode(), isAlwaysOnTop(), mAttrs.type, rawInsetsState);
+                .enforceInsetsPolicyForTarget(
+                        getWindowingMode(), isAlwaysOnTop(), mAttrs, rawInsetsState);
         return insetsPolicy.adjustInsetsForWindow(this, insetsStateForWindow,
                 includeTransient);
     }
@@ -5713,6 +5709,15 @@
         return super.getAnimationLeashParent();
     }
 
+    @Override
+    public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+        super.onAnimationLeashCreated(t, leash);
+        if (isStartingWindowAssociatedToTask()) {
+            // Make sure the animation leash is still on top of the task.
+            t.setLayer(leash, Integer.MAX_VALUE);
+        }
+    }
+
     // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA
     // then we can drop all negative layering on the windowing side and simply inherit
     // the default implementation here.
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 3f380e7..0d87237 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,6 +107,7 @@
     jmethodID notifyFocusChanged;
     jmethodID notifySensorEvent;
     jmethodID notifySensorAccuracy;
+    jmethodID notifyStylusGestureStarted;
     jmethodID notifyVibratorState;
     jmethodID filterInputEvent;
     jmethodID interceptKeyBeforeQueueing;
@@ -299,6 +300,7 @@
     void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled);
     void setCustomPointerIcon(const SpriteIcon& icon);
     void setMotionClassifierEnabled(bool enabled);
+    std::optional<std::string> getBluetoothAddress(int32_t deviceId);
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -312,6 +314,7 @@
                                                            int32_t surfaceRotation) override;
 
     TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr);
+    void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -370,37 +373,37 @@
     Mutex mLock;
     struct Locked {
         // Display size information.
-        std::vector<DisplayViewport> viewports;
+        std::vector<DisplayViewport> viewports{};
 
         // True if System UI is less noticeable.
-        bool systemUiLightsOut;
+        bool systemUiLightsOut{false};
 
         // Pointer speed.
-        int32_t pointerSpeed;
+        int32_t pointerSpeed{0};
 
         // Pointer acceleration.
-        float pointerAcceleration;
+        float pointerAcceleration{android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION};
 
         // True if pointer gestures are enabled.
-        bool pointerGesturesEnabled;
+        bool pointerGesturesEnabled{true};
 
         // Show touches feature enable/disable.
-        bool showTouches;
+        bool showTouches{false};
 
         // The latest request to enable or disable Pointer Capture.
-        PointerCaptureRequest pointerCaptureRequest;
+        PointerCaptureRequest pointerCaptureRequest{};
 
         // Sprite controller singleton, created on first use.
-        sp<SpriteController> spriteController;
+        sp<SpriteController> spriteController{};
 
         // Pointer controller singleton, created and destroyed as needed.
-        std::weak_ptr<PointerController> pointerController;
+        std::weak_ptr<PointerController> pointerController{};
 
         // Input devices to be disabled
-        std::set<int32_t> disabledInputDevices;
+        std::set<int32_t> disabledInputDevices{};
 
         // Associated Pointer controller display.
-        int32_t pointerDisplayId;
+        int32_t pointerDisplayId{ADISPLAY_ID_DEFAULT};
     } mLocked GUARDED_BY(mLock);
 
     std::atomic<bool> mInteractive;
@@ -419,16 +422,6 @@
 
     mServiceObj = env->NewGlobalRef(serviceObj);
 
-    {
-        AutoMutex _l(mLock);
-        mLocked.systemUiLightsOut = false;
-        mLocked.pointerSpeed = 0;
-        mLocked.pointerAcceleration = android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION;
-        mLocked.pointerGesturesEnabled = true;
-        mLocked.showTouches = false;
-        mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
-    }
-    mInteractive = true;
     InputManager* im = new InputManager(this, this);
     mInputManager = im;
     defaultServiceManager()->addService(String16("inputflinger"), im);
@@ -1177,6 +1170,13 @@
     return transform;
 }
 
+void NativeInputManager::notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) {
+    JNIEnv* env = jniEnv();
+    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyStylusGestureStarted, deviceId,
+                        eventTime);
+    checkAndClearExceptionFromCallback(env, "notifyStylusGestureStarted");
+}
+
 bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
     ATRACE_CALL();
     jobject inputEventObj;
@@ -1487,6 +1487,10 @@
     mInputManager->getProcessor().setMotionClassifierEnabled(enabled);
 }
 
+std::optional<std::string> NativeInputManager::getBluetoothAddress(int32_t deviceId) {
+    return mInputManager->getReader().getBluetoothAddress(deviceId);
+}
+
 bool NativeInputManager::isPerDisplayTouchModeEnabled() {
     JNIEnv* env = jniEnv();
     jboolean enabled =
@@ -2326,6 +2330,12 @@
     im->setPointerDisplayId(displayId);
 }
 
+static jstring nativeGetBluetoothAddress(JNIEnv* env, jobject nativeImplObj, jint deviceId) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+    const auto address = im->getBluetoothAddress(deviceId);
+    return address ? env->NewStringUTF(address->c_str()) : nullptr;
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gInputManagerMethods[] = {
@@ -2408,6 +2418,7 @@
         {"flushSensor", "(II)Z", (void*)nativeFlushSensor},
         {"cancelCurrentTouch", "()V", (void*)nativeCancelCurrentTouch},
         {"setPointerDisplayId", "(I)V", (void*)nativeSetPointerDisplayId},
+        {"getBluetoothAddress", "(I)Ljava/lang/String;", (void*)nativeGetBluetoothAddress},
 };
 
 #define FIND_CLASS(var, className) \
@@ -2469,6 +2480,9 @@
 
     GET_METHOD_ID(gServiceClassInfo.notifySensorAccuracy, clazz, "notifySensorAccuracy", "(III)V");
 
+    GET_METHOD_ID(gServiceClassInfo.notifyStylusGestureStarted, clazz, "notifyStylusGestureStarted",
+                  "(IJ)V");
+
     GET_METHOD_ID(gServiceClassInfo.notifyVibratorState, clazz, "notifyVibratorState", "(IZ)V");
 
     GET_METHOD_ID(gServiceClassInfo.notifyNoFocusedWindowAnr, clazz, "notifyNoFocusedWindowAnr",
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 91f5c69..40412db 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -21,20 +21,28 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.credentials.CreateCredentialRequest;
 import android.credentials.GetCredentialRequest;
 import android.credentials.ICreateCredentialCallback;
 import android.credentials.ICredentialManager;
 import android.credentials.IGetCredentialCallback;
+import android.os.Binder;
 import android.os.CancellationSignal;
 import android.os.ICancellationSignal;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.server.infra.AbstractMasterSystemService;
 import com.android.server.infra.SecureSettingsServiceNameResolver;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
 /**
  * Entry point service for credential management.
  *
@@ -49,52 +57,99 @@
 
     public CredentialManagerService(@NonNull Context context) {
         super(context,
-                new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE),
+                new SecureSettingsServiceNameResolver(context, Settings.Secure.CREDENTIAL_SERVICE,
+                        /*isMultipleMode=*/true),
                 null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
     }
 
     @Override
     protected String getServiceSettingsProperty() {
-        return Settings.Secure.AUTOFILL_SERVICE;
+        return Settings.Secure.CREDENTIAL_SERVICE;
     }
 
     @Override // from AbstractMasterSystemService
     protected CredentialManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
             boolean disabled) {
-        return new CredentialManagerServiceImpl(this, mLock, resolvedUserId);
+        // This method should not be called for CredentialManagerService as it is configured to use
+        // multiple services.
+        Slog.w(TAG, "Should not be here - CredentialManagerService is configured to use "
+                + "multiple services");
+        return null;
     }
 
-    @Override
+    @Override // from SystemService
     public void onStart() {
-        Log.i(TAG, "onStart");
         publishBinderService(CREDENTIAL_SERVICE, new CredentialManagerServiceStub());
     }
 
+    @Override // from AbstractMasterSystemService
+    protected List<CredentialManagerServiceImpl> newServiceListLocked(int resolvedUserId,
+            boolean disabled, String[] serviceNames) {
+        if (serviceNames == null || serviceNames.length == 0) {
+            Slog.i(TAG, "serviceNames sent in newServiceListLocked is null, or empty");
+            return new ArrayList<>();
+        }
+        List<CredentialManagerServiceImpl> serviceList = new ArrayList<>(serviceNames.length);
+        for (int i = 0; i < serviceNames.length; i++) {
+            Log.i(TAG, "in newServiceListLocked, service: " + serviceNames[i]);
+            if (TextUtils.isEmpty(serviceNames[i])) {
+                continue;
+            }
+            try {
+                serviceList.add(new CredentialManagerServiceImpl(this, mLock, resolvedUserId,
+                        serviceNames[i]));
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.i(TAG, "Unable to add serviceInfo : " + e.getMessage());
+            } catch (SecurityException e) {
+                Log.i(TAG, "Unable to add serviceInfo : " + e.getMessage());
+            }
+        }
+        return serviceList;
+    }
+
+    private void runForUser(@NonNull final Consumer<CredentialManagerServiceImpl> c) {
+        final int userId = UserHandle.getCallingUserId();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                final List<CredentialManagerServiceImpl> services =
+                        getServiceListForUserLocked(userId);
+                services.forEach(s -> {
+                    c.accept(s);
+                });
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
     final class CredentialManagerServiceStub extends ICredentialManager.Stub {
         @Override
         public ICancellationSignal executeGetCredential(
                 GetCredentialRequest request,
-                IGetCredentialCallback callback) {
-            // TODO: implement.
-            Log.i(TAG, "executeGetCredential");
-
-            final int userId = UserHandle.getCallingUserId();
-            synchronized (mLock) {
-                final CredentialManagerServiceImpl service = peekServiceForUserLocked(userId);
-                if (service != null) {
-                    Log.i(TAG, "Got service for : " + userId);
-                    service.getCredential();
-                }
-            }
-
+                IGetCredentialCallback callback,
+                final String callingPackage) {
+            Log.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
+            // TODO : Implement cancellation
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+
+            // New request session, scoped for this request only.
+            final GetRequestSession session = new GetRequestSession(getContext(),
+                    UserHandle.getCallingUserId(),
+                    callback);
+
+            // Invoke all services of a user
+            runForUser((service) -> {
+                service.getCredential(request, session, callingPackage);
+            });
             return cancelTransport;
         }
 
         @Override
         public ICancellationSignal executeCreateCredential(
                 CreateCredentialRequest request,
-                ICreateCredentialCallback callback) {
+                ICreateCredentialCallback callback,
+                String callingPackage) {
             // TODO: implement.
             Log.i(TAG, "executeCreateCredential");
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
index aa19241..cc03f9b 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
@@ -17,47 +17,94 @@
 package com.android.server.credentials;
 
 import android.annotation.NonNull;
-import android.app.AppGlobals;
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
-import android.os.RemoteException;
-import android.util.Log;
+import android.credentials.GetCredentialRequest;
+import android.service.credentials.CredentialProviderInfo;
+import android.service.credentials.GetCredentialsRequest;
+import android.util.Slog;
 
 import com.android.server.infra.AbstractPerUserSystemService;
 
+
 /**
- * Per-user implementation of {@link CredentialManagerService}
+ * Per-user, per remote service implementation of {@link CredentialManagerService}
  */
 public final class CredentialManagerServiceImpl extends
         AbstractPerUserSystemService<CredentialManagerServiceImpl, CredentialManagerService> {
     private static final String TAG = "CredManSysServiceImpl";
 
-    protected CredentialManagerServiceImpl(
+    // TODO(b/210531) : Make final when update flow is fixed
+    private ComponentName mRemoteServiceComponentName;
+    private CredentialProviderInfo mInfo;
+
+    public CredentialManagerServiceImpl(
             @NonNull CredentialManagerService master,
-            @NonNull Object lock, int userId) {
+            @NonNull Object lock, int userId, String serviceName)
+            throws PackageManager.NameNotFoundException {
         super(master, lock, userId);
+        Slog.i(TAG, "in CredentialManagerServiceImpl cons");
+        // TODO : Replace with newServiceInfoLocked after confirming behavior
+        mRemoteServiceComponentName = ComponentName.unflattenFromString(serviceName);
+        mInfo = new CredentialProviderInfo(getContext(), mRemoteServiceComponentName, mUserId);
     }
 
     @Override // from PerUserSystemService
     protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
             throws PackageManager.NameNotFoundException {
-        ServiceInfo si;
-        try {
-            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
-                    PackageManager.GET_META_DATA, mUserId);
-        } catch (RemoteException e) {
-            throw new PackageManager.NameNotFoundException(
-                    "Could not get service for " + serviceComponent);
-        }
-        return si;
+        // TODO : Test update flows with multiple providers
+        Slog.i(TAG , "newServiceInfoLocked with : " + serviceComponent.getPackageName());
+        mRemoteServiceComponentName = serviceComponent;
+        mInfo = new CredentialProviderInfo(getContext(), serviceComponent, mUserId);
+        return mInfo.getServiceInfo();
     }
 
-    /**
-     * Unimplemented getCredentials
-     */
-    public void getCredential() {
-        Log.i(TAG, "getCredential not implemented");
-        // TODO : Implement logic
+    public void getCredential(GetCredentialRequest request, GetRequestSession requestSession,
+            String callingPackage) {
+        Slog.i(TAG, "in getCredential in CredManServiceImpl");
+        if (mInfo == null) {
+            Slog.i(TAG, "in getCredential in CredManServiceImpl, but mInfo is null");
+            return;
+        }
+
+        // TODO : Determine if remoteService instance can be reused across requests
+        final RemoteCredentialService remoteService = new RemoteCredentialService(
+                getContext(), mInfo.getServiceInfo().getComponentName(), mUserId);
+        ProviderGetSession providerSession = new ProviderGetSession(mInfo,
+                requestSession, mUserId, remoteService);
+        // Set the provider info to the session when the request is initiated. This happens here
+        // because there is one serviceImpl per remote provider, and so we can only retrieve
+        // the provider information in the scope of this instance, whereas the session is for the
+        // entire request.
+        requestSession.addProviderSession(providerSession);
+        GetCredentialsRequest filteredRequest = getRequestWithValidType(request, callingPackage);
+        if (filteredRequest != null) {
+            remoteService.onGetCredentials(getRequestWithValidType(request, callingPackage),
+                    providerSession);
+        }
+    }
+
+    @Nullable
+    private GetCredentialsRequest getRequestWithValidType(GetCredentialRequest request,
+            String callingPackage) {
+        GetCredentialsRequest.Builder builder =
+                new GetCredentialsRequest.Builder(callingPackage);
+        request.getGetCredentialOptions().forEach( option -> {
+            if (mInfo.hasCapability(option.getType())) {
+                Slog.i(TAG, "Provider can handle: " + option.getType());
+                builder.addGetCredentialOption(option);
+            } else {
+                Slog.i(TAG, "Skipping request as provider cannot handle it");
+            }
+        });
+
+        try {
+            return builder.build();
+        } catch (IllegalArgumentException | NullPointerException e) {
+            Slog.i(TAG, "issue with request build: " + e.getMessage());
+        }
+        return null;
     }
 }
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
new file mode 100644
index 0000000..5f25e3d
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -0,0 +1,91 @@
+/*
+ * 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.credentials;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
+import android.credentials.ui.IntentFactory;
+import android.credentials.ui.ProviderData;
+import android.credentials.ui.RequestInfo;
+import android.credentials.ui.UserSelectionDialogResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/** Initiates the Credential Manager UI and receives results. */
+public class CredentialManagerUi {
+    private static final String TAG = "CredentialManagerUi";
+    @NonNull
+    private final CredentialManagerUiCallback mCallbacks;
+    @NonNull private final Context mContext;
+    private final int mUserId;
+    @NonNull private final ResultReceiver mResultReceiver = new ResultReceiver(
+            new Handler(Looper.getMainLooper())) {
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            handleUiResult(resultCode, resultData);
+        }
+    };
+
+    private void handleUiResult(int resultCode, Bundle resultData) {
+        if (resultCode == UserSelectionDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION) {
+            UserSelectionDialogResult selection = UserSelectionDialogResult
+                    .fromResultData(resultData);
+            if (selection != null) {
+                mCallbacks.onUiSelection(selection);
+            } else {
+                Slog.i(TAG, "No selection found in UI result");
+            }
+        } else if (resultCode == UserSelectionDialogResult.RESULT_CODE_DIALOG_CANCELED) {
+            mCallbacks.onUiCancelation();
+        }
+    }
+
+    /**
+     * Interface to be implemented by any class that wishes to get callbacks from the UI.
+     */
+    public interface CredentialManagerUiCallback {
+        /** Called when the user makes a selection. */
+        void onUiSelection(UserSelectionDialogResult selection);
+        /** Called when the user cancels the UI. */
+        void onUiCancelation();
+    }
+    public CredentialManagerUi(Context context, int userId,
+            CredentialManagerUiCallback callbacks) {
+        Log.i(TAG, "In CredentialManagerUi constructor");
+        mContext = context;
+        mUserId = userId;
+        mCallbacks = callbacks;
+    }
+
+    /**
+     * Surfaces the Credential Manager bottom sheet UI.
+     * @param providerDataList the list of provider data from remote providers
+     */
+    public void show(RequestInfo requestInfo, ArrayList<ProviderData> providerDataList) {
+        Log.i(TAG, "In show");
+        Intent intent = IntentFactory.newIntent(requestInfo, providerDataList,
+                mResultReceiver);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+    }
+}
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
new file mode 100644
index 0000000..80f0fec
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -0,0 +1,164 @@
+/*
+ * 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.credentials;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.credentials.Credential;
+import android.credentials.GetCredentialResponse;
+import android.credentials.IGetCredentialCallback;
+import android.credentials.ui.ProviderData;
+import android.credentials.ui.RequestInfo;
+import android.credentials.ui.UserSelectionDialogResult;
+import android.os.RemoteException;
+import android.service.credentials.CredentialEntry;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Central session for a single getCredentials request. This class listens to the
+ * responses from providers, and the UX app, and updates the provider(S) state.
+ */
+public final class GetRequestSession extends RequestSession {
+    private static final String TAG = "GetRequestSession";
+
+    private final IGetCredentialCallback mClientCallback;
+    private final Map<String, ProviderGetSession> mProviders;
+
+    public GetRequestSession(Context context, int userId,
+            IGetCredentialCallback callback) {
+        super(context, userId, RequestInfo.TYPE_GET);
+        mClientCallback = callback;
+        mProviders = new HashMap<>();
+    }
+
+    /**
+     * Adds a new provider to the list of providers that are contributing to this session.
+     */
+    public void addProviderSession(ProviderGetSession providerSession) {
+        mProviders.put(providerSession.getComponentName().flattenToString(),
+                providerSession);
+    }
+
+    @Override
+    public void onProviderStatusChanged(ProviderSession.Status status,
+            ComponentName componentName) {
+        Log.i(TAG, "in onStatusChanged");
+        if (ProviderSession.isTerminatingStatus(status)) {
+            Log.i(TAG, "in onStatusChanged terminating status");
+
+            ProviderGetSession session = mProviders.remove(componentName.flattenToString());
+            if (session != null) {
+                Slog.i(TAG, "Provider session removed.");
+            } else {
+                Slog.i(TAG, "Provider session null, did not exist.");
+            }
+        } else if (ProviderSession.isCompletionStatus(status)) {
+            Log.i(TAG, "in onStatusChanged isCompletionStatus status");
+            onProviderResponseComplete();
+        }
+    }
+
+    @Override
+    public void onUiSelection(UserSelectionDialogResult selection) {
+        String providerId = selection.getProviderId();
+        ProviderGetSession providerSession = mProviders.get(providerId);
+        if (providerSession != null) {
+            CredentialEntry credentialEntry = providerSession.getCredentialEntry(
+                    selection.getEntrySubkey());
+            if (credentialEntry != null && credentialEntry.getCredential() != null) {
+                respondToClientAndFinish(credentialEntry.getCredential());
+            }
+            // TODO : Handle action chips and authentication selection
+            return;
+        }
+        // TODO : finish session and respond to client if provider not found
+    }
+
+    @Override
+    public void onUiCancelation() {
+        // User canceled the activity
+        // TODO : Send error code to client
+        finishSession();
+    }
+
+    private void onProviderResponseComplete() {
+        Log.i(TAG, "in onProviderResponseComplete");
+        if (isResponseCompleteAcrossProviders()) {
+            Log.i(TAG, "in onProviderResponseComplete - isResponseCompleteAcrossProviders");
+            getProviderDataAndInitiateUi();
+        }
+    }
+
+    private void getProviderDataAndInitiateUi() {
+        ArrayList<ProviderData> providerDataList = new ArrayList<>();
+        for (ProviderGetSession session : mProviders.values()) {
+            Log.i(TAG, "preparing data for : " + session.getComponentName());
+            providerDataList.add(session.prepareUiData());
+        }
+        if (!providerDataList.isEmpty()) {
+            Log.i(TAG, "provider list not empty about to initiate ui");
+            initiateUi(providerDataList);
+        }
+    }
+
+    private void initiateUi(ArrayList<ProviderData> providerDataList) {
+        mHandler.post(() -> mCredentialManagerUi.show(RequestInfo.newGetRequestInfo(
+                mRequestId, null, mIsFirstUiTurn, ""),
+                providerDataList));
+    }
+
+    /**
+     * Iterates over all provider sessions and returns true if all have responded.
+     */
+    private boolean isResponseCompleteAcrossProviders() {
+        AtomicBoolean isRequestComplete = new AtomicBoolean(true);
+        mProviders.forEach( (packageName, session) -> {
+            if (session.getStatus() != ProviderSession.Status.COMPLETE) {
+                isRequestComplete.set(false);
+            }
+        });
+        return isRequestComplete.get();
+    }
+
+    private void respondToClientAndFinish(Credential credential) {
+        try {
+            mClientCallback.onResponse(new GetCredentialResponse(credential));
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+        finishSession();
+    }
+
+    private void finishSession() {
+        clearProviderSessions();
+    }
+
+    private void clearProviderSessions() {
+        for (ProviderGetSession session : mProviders.values()) {
+            // TODO : Evaluate if we should unbind remote services here or wait for them
+            // to automatically unbind when idle. Re-binding frequently also has a cost.
+            //session.destroy();
+        }
+        mProviders.clear();
+    }
+}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
new file mode 100644
index 0000000..24610df
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -0,0 +1,197 @@
+/*
+ * 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.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.slice.Slice;
+import android.credentials.ui.Entry;
+import android.credentials.ui.ProviderData;
+import android.service.credentials.Action;
+import android.service.credentials.CredentialEntry;
+import android.service.credentials.CredentialProviderInfo;
+import android.service.credentials.CredentialsDisplayContent;
+import android.service.credentials.GetCredentialsResponse;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Central provider session that listens for provider callbacks, and maintains provider state.
+ * Will likely split this into remote response state and UI state.
+ */
+public final class ProviderGetSession extends ProviderSession<GetCredentialsResponse>
+        implements RemoteCredentialService.ProviderCallbacks<GetCredentialsResponse> {
+    private static final String TAG = "ProviderGetSession";
+
+    // Key to be used as an entry key for a credential entry
+    private static final String CREDENTIAL_ENTRY_KEY = "credential_key";
+
+    private GetCredentialsResponse mResponse;
+
+    @NonNull
+    private final Map<String, CredentialEntry> mUiCredentials = new HashMap<>();
+
+    @NonNull
+    private final Map<String, Action> mUiActions = new HashMap<>();
+
+    public ProviderGetSession(CredentialProviderInfo info,
+            ProviderInternalCallback callbacks,
+            int userId, RemoteCredentialService remoteCredentialService) {
+        super(info, callbacks, userId, remoteCredentialService);
+        setStatus(Status.PENDING);
+    }
+
+    /** Updates the response being maintained in state by this provider session. */
+    @Override
+    public void updateResponse(GetCredentialsResponse response) {
+        if (response.getAuthenticationAction() != null) {
+            // TODO : Implement authentication logic
+        } else if (response.getCredentialsDisplayContent() != null) {
+            Log.i(TAG , "updateResponse with credentialEntries");
+            mResponse = response;
+            updateStatusAndInvokeCallback(Status.COMPLETE);
+        }
+    }
+
+    /** Returns the response being maintained in this provider session. */
+    @Override
+    @Nullable
+    public GetCredentialsResponse getResponse() {
+        return  mResponse;
+    }
+
+    /** Returns the credential entry maintained in state by this provider session. */
+    @Nullable
+    public CredentialEntry getCredentialEntry(@NonNull String entryId) {
+        return mUiCredentials.get(entryId);
+    }
+
+    /** Returns the action entry maintained in state by this provider session. */
+    @Nullable
+    public Action getAction(@NonNull String entryId) {
+        return mUiActions.get(entryId);
+    }
+
+    /** Called when the provider response has been updated by an external source. */
+    @Override
+    public void onProviderResponseSuccess(@Nullable GetCredentialsResponse response) {
+        Log.i(TAG, "in onProviderResponseSuccess");
+        updateResponse(response);
+    }
+
+    /** Called when the provider response resulted in a failure. */
+    @Override
+    public void onProviderResponseFailure(int errorCode, @Nullable CharSequence message) {
+        updateStatusAndInvokeCallback(toStatus(errorCode));
+    }
+
+    /** Called when provider service dies. */
+    @Override
+    public void onProviderServiceDied(RemoteCredentialService service) {
+        if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+            updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
+        } else {
+            Slog.i(TAG, "Component names different in onProviderServiceDied - "
+                    + "this should not happen");
+        }
+    }
+
+    @Override
+    protected final ProviderData prepareUiData() throws IllegalArgumentException {
+        Log.i(TAG, "In prepareUiData");
+        if (!ProviderSession.isCompletionStatus(getStatus())) {
+            Log.i(TAG, "In prepareUiData not complete");
+
+            throw new IllegalStateException("Status must be in completion mode");
+        }
+        GetCredentialsResponse response = getResponse();
+        if (response == null) {
+            Log.i(TAG, "In prepareUiData response null");
+
+            throw new IllegalStateException("Response must be in completion mode");
+        }
+        if (response.getAuthenticationAction() != null) {
+            Log.i(TAG, "In prepareUiData auth not null");
+
+            return prepareUiProviderDataWithAuthentication(response.getAuthenticationAction());
+        }
+        if (response.getCredentialsDisplayContent() != null){
+            Log.i(TAG, "In prepareUiData credentials not null");
+
+            return prepareUiProviderDataWithCredentials(response.getCredentialsDisplayContent());
+        }
+        return null;
+    }
+
+    /**
+     * To be called by {@link ProviderGetSession} when the UI is to be invoked.
+     */
+    @Nullable
+    private ProviderData prepareUiProviderDataWithCredentials(@NonNull
+            CredentialsDisplayContent content) {
+        Log.i(TAG, "in prepareUiProviderData");
+        List<Entry> credentialEntries = new ArrayList<>();
+        List<Entry> actionChips = new ArrayList<>();
+        Entry authenticationEntry = null;
+
+        // Populate the credential entries
+        for (CredentialEntry credentialEntry : content.getCredentialEntries()) {
+            String entryId = UUID.randomUUID().toString();
+            mUiCredentials.put(entryId, credentialEntry);
+            Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
+            Slice slice = credentialEntry.getSlice();
+            // TODO : Remove conversion of string to int after change in Entry class
+            credentialEntries.add(new Entry(CREDENTIAL_ENTRY_KEY, entryId,
+                    credentialEntry.getSlice()));
+        }
+        // populate the action chip
+        for (Action action : content.getActions()) {
+            String entryId = UUID.randomUUID().toString();
+            mUiActions.put(entryId, action);
+            // TODO : Remove conversion of string to int after change in Entry class
+            actionChips.add(new Entry(ACTION_ENTRY_KEY, entryId,
+                    action.getSlice()));
+        }
+
+        // TODO : Set the correct last used time
+        return new ProviderData.Builder(mComponentName.flattenToString(),
+                mProviderInfo.getServiceLabel() == null ? "" :
+                        mProviderInfo.getServiceLabel().toString(),
+                /*icon=*/null)
+                .setCredentialEntries(credentialEntries)
+                .setActionChips(actionChips)
+                .setAuthenticationEntry(authenticationEntry)
+                .setLastUsedTimeMillis(0)
+                .build();
+    }
+
+    /**
+     * To be called by {@link ProviderGetSession} when the UI is to be invoked.
+     */
+    @Nullable
+    private ProviderData prepareUiProviderDataWithAuthentication(@NonNull
+            Action authenticationEntry) {
+        // TODO : Implement authentication flow
+        return null;
+    }
+}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
new file mode 100644
index 0000000..3a9f964
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.credentials.ui.ProviderData;
+import android.service.credentials.CredentialProviderException;
+import android.service.credentials.CredentialProviderInfo;
+
+/**
+ * Provider session storing the state of provider response and ui entries.
+ * @param <T> The request type expected from the remote provider, for a given request session.
+ */
+public abstract class ProviderSession<T> implements RemoteCredentialService.ProviderCallbacks<T> {
+    // Key to be used as the entry key for an action entry
+    protected static final String ACTION_ENTRY_KEY = "action_key";
+
+    @NonNull protected final ComponentName mComponentName;
+    @NonNull protected final CredentialProviderInfo mProviderInfo;
+    @NonNull protected final RemoteCredentialService mRemoteCredentialService;
+    @NonNull protected final int mUserId;
+    @NonNull protected Status mStatus = Status.NOT_STARTED;
+    @NonNull protected final ProviderInternalCallback mCallbacks;
+
+    /**
+     * Interface to be implemented by any class that wishes to get a callback when a particular
+     * provider session's status changes. Typically, implemented by the {@link RequestSession}
+     * class.
+     */
+    public interface ProviderInternalCallback {
+        /**
+         * Called when status changes.
+         */
+        void onProviderStatusChanged(Status status, ComponentName componentName);
+    }
+
+    protected ProviderSession(@NonNull CredentialProviderInfo info,
+            @NonNull ProviderInternalCallback callbacks,
+            @NonNull int userId,
+            @NonNull RemoteCredentialService remoteCredentialService) {
+        mProviderInfo = info;
+        mCallbacks = callbacks;
+        mUserId = userId;
+        mComponentName = info.getServiceInfo().getComponentName();
+        mRemoteCredentialService = remoteCredentialService;
+    }
+
+    /** Update the response state stored with the provider session. */
+    protected abstract void updateResponse (T response);
+
+    /** Update the response state stored with the provider session. */
+    protected abstract T getResponse ();
+
+    /** Should be overridden to prepare, and stores state for {@link ProviderData} to be
+     * shown on the UI. */
+    protected abstract ProviderData prepareUiData();
+
+    /** Provider status at various states of the request session. */
+    enum Status {
+        NOT_STARTED,
+        PENDING,
+        REQUIRES_AUTHENTICATION,
+        COMPLETE,
+        SERVICE_DEAD,
+        CANCELED
+    }
+
+    protected void setStatus(@NonNull Status status) {
+        mStatus = status;
+    }
+
+    @NonNull
+    protected Status getStatus() {
+        return mStatus;
+    }
+
+    @NonNull
+    protected ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /** Updates the status .*/
+    protected void updateStatusAndInvokeCallback(@NonNull Status status) {
+        setStatus(status);
+        mCallbacks.onProviderStatusChanged(status, mComponentName);
+    }
+
+    @NonNull
+    public static Status toStatus(
+            @CredentialProviderException.CredentialProviderError int errorCode) {
+        // TODO : Add more mappings as more flows are supported
+        return Status.CANCELED;
+    }
+
+    /**
+     * Returns true if the given status means that the provider session must be terminated.
+     */
+    public static boolean isTerminatingStatus(Status status) {
+        return status == Status.CANCELED || status == Status.SERVICE_DEAD;
+    }
+
+    /**
+     * Returns true if the given status means that the provider is done getting the response,
+     * and is ready for user interaction.
+     */
+    public static boolean isCompletionStatus(Status status) {
+        return status == Status.COMPLETE || status == Status.REQUIRES_AUTHENTICATION;
+    }
+}
diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
new file mode 100644
index 0000000..d0b6e7d
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
@@ -0,0 +1,185 @@
+/*
+ * 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.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.RemoteException;
+import android.service.credentials.CredentialProviderException;
+import android.service.credentials.CredentialProviderException.CredentialProviderError;
+import android.service.credentials.CredentialProviderService;
+import android.service.credentials.GetCredentialsRequest;
+import android.service.credentials.GetCredentialsResponse;
+import android.service.credentials.ICredentialProviderService;
+import android.service.credentials.IGetCredentialsCallback;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.infra.ServiceConnector;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Handles connections with the remote credential provider
+ *
+ * @hide
+ */
+public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialProviderService>{
+
+    private static final String TAG = "RemoteCredentialService";
+    /** Timeout for a single request. */
+    private static final long TIMEOUT_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
+    /** Timeout to unbind after the task queue is empty. */
+    private static final long TIMEOUT_IDLE_SERVICE_CONNECTION_MILLIS =
+            5 * DateUtils.SECOND_IN_MILLIS;
+
+    private final ComponentName mComponentName;
+
+    /**
+     * Callbacks to be invoked when the provider remote service responds with a
+     * success or failure.
+     * @param <T> the type of response expected from the provider
+     */
+    public interface ProviderCallbacks<T> {
+        /** Called when a successful response is received from the remote provider. */
+        void onProviderResponseSuccess(@Nullable T response);
+        /** Called when a failure response is received from the remote provider. */
+        void onProviderResponseFailure(int errorCode, @Nullable CharSequence message);
+        /** Called when the remote provider service dies. */
+        void onProviderServiceDied(RemoteCredentialService service);
+    }
+
+    public RemoteCredentialService(@NonNull Context context,
+            @NonNull ComponentName componentName, int userId) {
+        super(context, new Intent(CredentialProviderService.SERVICE_INTERFACE)
+                        .setComponent(componentName), Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
+                userId, ICredentialProviderService.Stub::asInterface);
+        mComponentName = componentName;
+    }
+
+    /** Unbinds automatically after this amount of time. */
+    @Override
+    protected long getAutoDisconnectTimeoutMs() {
+        return TIMEOUT_IDLE_SERVICE_CONNECTION_MILLIS;
+    }
+
+    /** Return the componentName of the service to be connected. */
+    @NonNull public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /** Destroys this remote service by unbinding the connection. */
+    public void destroy() {
+        unbind();
+    }
+
+    /** Main entry point to be called for executing a getCredential call on the remote
+     * provider service.
+     * @param request the request to be sent to the provider
+     * @param callback the callback to be used to send back the provider response to the
+     *                 {@link ProviderSession} class that maintains provider state
+     */
+    public void onGetCredentials(@NonNull GetCredentialsRequest request,
+            ProviderCallbacks<GetCredentialsResponse> callback) {
+        Log.i(TAG, "In onGetCredentials in RemoteCredentialService");
+        AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
+        AtomicReference<CompletableFuture<GetCredentialsResponse>> futureRef =
+                new AtomicReference<>();
+
+        CompletableFuture<GetCredentialsResponse> connectThenExecute = postAsync(service -> {
+            CompletableFuture<GetCredentialsResponse> getCredentials = new CompletableFuture<>();
+            ICancellationSignal cancellationSignal =
+                    service.onGetCredentials(request, new IGetCredentialsCallback.Stub() {
+                @Override
+                public void onSuccess(GetCredentialsResponse response) {
+                    Log.i(TAG, "In onSuccess in RemoteCredentialService");
+                    getCredentials.complete(response);
+                }
+
+                @Override
+                public void onFailure(@CredentialProviderError int errorCode,
+                        CharSequence message) {
+                    Log.i(TAG, "In onFailure in RemoteCredentialService");
+                    String errorMsg = message == null ? "" : String.valueOf(message);
+                    getCredentials.completeExceptionally(new CredentialProviderException(
+                            errorCode, errorMsg));
+                }
+            });
+            CompletableFuture<GetCredentialsResponse> future = futureRef.get();
+            if (future != null && future.isCancelled()) {
+                dispatchCancellationSignal(cancellationSignal);
+            } else {
+                cancellationSink.set(cancellationSignal);
+            }
+            return getCredentials;
+        }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+        futureRef.set(connectThenExecute);
+
+        connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() -> {
+            if (error == null) {
+                Log.i(TAG, "In RemoteCredentialService execute error is null");
+                callback.onProviderResponseSuccess(result);
+            } else {
+                if (error instanceof TimeoutException) {
+                    Log.i(TAG, "In RemoteCredentialService execute error is timeout");
+                    dispatchCancellationSignal(cancellationSink.get());
+                    callback.onProviderResponseFailure(
+                            CredentialProviderException.ERROR_TIMEOUT,
+                            error.getMessage());
+                } else if (error instanceof CancellationException) {
+                    Log.i(TAG, "In RemoteCredentialService execute error is cancellation");
+                    dispatchCancellationSignal(cancellationSink.get());
+                    callback.onProviderResponseFailure(
+                            CredentialProviderException.ERROR_TASK_CANCELED,
+                            error.getMessage());
+                } else if (error instanceof CredentialProviderException) {
+                    Log.i(TAG, "In RemoteCredentialService execute error is provider error");
+                    callback.onProviderResponseFailure(((CredentialProviderException) error)
+                                    .getErrorCode(),
+                            error.getMessage());
+                } else {
+                    Log.i(TAG, "In RemoteCredentialService execute error is unknown");
+                    callback.onProviderResponseFailure(
+                            CredentialProviderException.ERROR_UNKNOWN,
+                            error.getMessage());
+                }
+            }
+        }));
+    }
+
+    private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) {
+        if (signal == null) {
+            Slog.e(TAG, "Error dispatching a cancellation - Signal is null");
+            return;
+        }
+        try {
+            signal.cancel();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error dispatching a cancellation", e);
+        }
+    }
+}
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
new file mode 100644
index 0000000..1bacbb3
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -0,0 +1,67 @@
+/*
+ * 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.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Context;
+import android.credentials.ui.UserSelectionDialogResult;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+
+/**
+ * Base class of a request session, that listens to UI events. This class must be extended
+ * every time a new response type is expected from the providers.
+ */
+abstract class RequestSession implements CredentialManagerUi.CredentialManagerUiCallback,
+        ProviderSession.ProviderInternalCallback {
+    @NonNull protected final IBinder mRequestId;
+    @NonNull protected final Context mContext;
+    @NonNull protected final CredentialManagerUi mCredentialManagerUi;
+    @NonNull protected final String mRequestType;
+    @NonNull protected final Handler mHandler;
+    @NonNull protected boolean mIsFirstUiTurn = true;
+    @UserIdInt protected final int mUserId;
+
+    protected RequestSession(@NonNull Context context,
+            @UserIdInt int userId, @NonNull String requestType) {
+        mContext = context;
+        mUserId = userId;
+        mRequestType = requestType;
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
+        mRequestId = new Binder();
+        mCredentialManagerUi = new CredentialManagerUi(mContext,
+                mUserId, this);
+    }
+
+    /** Returns the unique identifier of this request session. */
+    public IBinder getRequestId() {
+        return mRequestId;
+    }
+
+    @Override // from CredentialManagerUiCallback
+    public abstract void onUiSelection(UserSelectionDialogResult selection);
+
+    @Override // from CredentialManagerUiCallback
+    public abstract void onUiCancelation();
+
+    @Override // from ProviderInternalCallback
+    public abstract void onProviderStatusChanged(ProviderSession.Status status, ComponentName componentName);
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b74fedf..593e648 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -154,6 +154,7 @@
 import com.android.server.people.PeopleService;
 import com.android.server.pm.ApexManager;
 import com.android.server.pm.ApexSystemServiceInfo;
+import com.android.server.pm.BackgroundInstallControlService;
 import com.android.server.pm.CrossProfileAppsService;
 import com.android.server.pm.DataLoaderManagerService;
 import com.android.server.pm.DynamicCodeLoggingService;
@@ -2469,6 +2470,10 @@
             t.traceBegin("StartMediaMetricsManager");
             mSystemServiceManager.startService(MediaMetricsManagerService.class);
             t.traceEnd();
+
+            t.traceBegin("StartBackgroundInstallControlService");
+            mSystemServiceManager.startService(BackgroundInstallControlService.class);
+            t.traceEnd();
         }
 
         t.traceBegin("StartMediaProjectionManager");
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 66524edf..35b9bc3 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -984,6 +984,7 @@
             monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
                     UserHandle.ALL, true);
         }
+
         private UserState getOrCreateUserStateLocked(int userId, boolean lowPriority) {
             return getOrCreateUserStateLocked(userId, lowPriority,
                     true /* enforceUserUnlockingOrUnlocked */);
@@ -991,6 +992,12 @@
 
         private UserState getOrCreateUserStateLocked(int userId, boolean lowPriority,
                 boolean enforceUserUnlockingOrUnlocked) {
+            return getOrCreateUserStateLocked(userId, lowPriority,
+                    enforceUserUnlockingOrUnlocked, false /* shouldUpdateState */);
+        }
+
+        private UserState getOrCreateUserStateLocked(int userId, boolean lowPriority,
+                boolean enforceUserUnlockingOrUnlocked, boolean shouldUpdateState) {
             if (enforceUserUnlockingOrUnlocked && !mUserManager.isUserUnlockingOrUnlocked(userId)) {
                 throw new IllegalStateException(
                         "User " + userId + " must be unlocked for printing to be available");
@@ -1000,6 +1007,8 @@
             if (userState == null) {
                 userState = new UserState(mContext, userId, mLock, lowPriority);
                 mUserStates.put(userId, userState);
+            } else if (shouldUpdateState) {
+                userState.updateIfNeededLocked();
             }
 
             if (!lowPriority) {
@@ -1019,9 +1028,9 @@
 
                     UserState userState;
                     synchronized (mLock) {
-                        userState = getOrCreateUserStateLocked(userId, true,
-                                false /*enforceUserUnlockingOrUnlocked */);
-                        userState.updateIfNeededLocked();
+                        userState = getOrCreateUserStateLocked(userId, /* lowPriority */ true,
+                                /* enforceUserUnlockingOrUnlocked */ false,
+                                /* shouldUpdateState */ true);
                     }
                     // This is the first time we switch to this user after boot, so
                     // now is the time to remove obsolete print jobs since they
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
new file mode 100644
index 0000000..939fb6a
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -0,0 +1,62 @@
+// 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "FrameworksInputMethodSystemServerTests",
+    defaults: [
+        "modules-utils-testable-device-config-defaults",
+    ],
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.runner",
+        "androidx.test.espresso.core",
+        "androidx.test.espresso.contrib",
+        "androidx.test.ext.truth",
+        "frameworks-base-testutils",
+        "mockito-target-extended-minus-junit4",
+        "platform-test-annotations",
+        "services.core",
+        "servicestests-core-utils",
+        "servicestests-utils-mockito-extended",
+        "truth-prebuilt",
+    ],
+
+    libs: [
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    certificate: "platform",
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/InputMethodSystemServerTests/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/AndroidManifest.xml
new file mode 100644
index 0000000..12e7cfc
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.frameworks.inputmethodtests">
+
+    <uses-sdk android:targetSdkVersion="31" />
+
+    <!-- Permissions required for granting and logging -->
+    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
+    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
+    <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
+    <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
+
+    <!-- Permissions for reading system info -->
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+
+    <application android:testOnly="true"
+                 android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.frameworks.inputmethodtests"
+        android:label="Frameworks InputMethod System Service Tests" />
+
+</manifest>
diff --git a/services/tests/InputMethodSystemServerTests/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/AndroidTest.xml
new file mode 100644
index 0000000..92be780
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Frameworks InputMethod System Services 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="FrameworksInputMethodSystemServerTests.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="FrameworksInputMethodSystemServerTests" />
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.inputmethodtests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+
+    <!-- Collect the files in the dump directory for debugging -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/sdcard/FrameworksInputMethodSystemServerTests/" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
+</configuration>
diff --git a/services/tests/InputMethodSystemServerTests/OWNERS b/services/tests/InputMethodSystemServerTests/OWNERS
new file mode 100644
index 0000000..1f2c036
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
new file mode 100644
index 0000000..3fbc400
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -0,0 +1,259 @@
+/*
+ * 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.inputmethod;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.view.inputmethod.EditorInfo;
+import android.window.ImeOnBackInvokedDispatcher;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.inputmethod.IInputMethod;
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.view.IInputMethodManager;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.SystemService;
+import com.android.server.input.InputManagerInternal;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/** Base class for testing {@link InputMethodManagerService}. */
+public class InputMethodManagerServiceTestBase {
+    protected static final String TEST_SELECTED_IME_ID = "test.ime";
+    protected static final String TEST_EDITOR_PKG_NAME = "test.editor";
+    protected static final String TEST_FOCUSED_WINDOW_NAME = "test.editor/activity";
+    protected static final WindowManagerInternal.ImeTargetInfo TEST_IME_TARGET_INFO =
+            new WindowManagerInternal.ImeTargetInfo(
+                    TEST_FOCUSED_WINDOW_NAME,
+                    TEST_FOCUSED_WINDOW_NAME,
+                    TEST_FOCUSED_WINDOW_NAME,
+                    TEST_FOCUSED_WINDOW_NAME,
+                    TEST_FOCUSED_WINDOW_NAME);
+    protected static final InputBindResult SUCCESS_WAITING_IME_BINDING_RESULT =
+            new InputBindResult(
+                    InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
+                    null,
+                    null,
+                    null,
+                    "0",
+                    0,
+                    null,
+                    false);
+
+    @Mock protected WindowManagerInternal mMockWindowManagerInternal;
+    @Mock protected ActivityManagerInternal mMockActivityManagerInternal;
+    @Mock protected PackageManagerInternal mMockPackageManagerInternal;
+    @Mock protected InputManagerInternal mMockInputManagerInternal;
+    @Mock protected DisplayManagerInternal mMockDisplayManagerInternal;
+    @Mock protected UserManagerInternal mMockUserManagerInternal;
+    @Mock protected InputMethodBindingController mMockInputMethodBindingController;
+    @Mock protected IInputMethodClient mMockInputMethodClient;
+    @Mock protected IBinder mWindowToken;
+    @Mock protected IRemoteInputConnection mMockRemoteInputConnection;
+    @Mock protected IRemoteAccessibilityInputConnection mMockRemoteAccessibilityInputConnection;
+    @Mock protected ImeOnBackInvokedDispatcher mMockImeOnBackInvokedDispatcher;
+    @Mock protected IInputMethodManager.Stub mMockIInputMethodManager;
+    @Mock protected IPlatformCompat.Stub mMockIPlatformCompat;
+    @Mock protected IInputMethod mMockInputMethod;
+    @Mock protected IBinder mMockInputMethodBinder;
+    @Mock protected IInputManager mMockIInputManager;
+
+    protected Context mContext;
+    protected MockitoSession mMockingSession;
+    protected int mTargetSdkVersion;
+    protected int mCallingUserId;
+    protected EditorInfo mEditorInfo;
+    protected IInputMethodInvoker mMockInputMethodInvoker;
+    protected InputMethodManagerService mInputMethodManagerService;
+    protected ServiceThread mServiceThread;
+
+    @Before
+    public void setUp() throws RemoteException {
+        mMockingSession =
+                mockitoSession()
+                        .initMocks(this)
+                        .strictness(Strictness.LENIENT)
+                        .mockStatic(LocalServices.class)
+                        .mockStatic(ServiceManager.class)
+                        .mockStatic(SystemServerInitThreadPool.class)
+                        .startMocking();
+
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        spyOn(mContext);
+
+        mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+        mCallingUserId = UserHandle.getCallingUserId();
+        mEditorInfo = new EditorInfo();
+        mEditorInfo.packageName = TEST_EDITOR_PKG_NAME;
+
+        // Injecting and mocking local services.
+        doReturn(mMockWindowManagerInternal)
+                .when(() -> LocalServices.getService(WindowManagerInternal.class));
+        doReturn(mMockActivityManagerInternal)
+                .when(() -> LocalServices.getService(ActivityManagerInternal.class));
+        doReturn(mMockPackageManagerInternal)
+                .when(() -> LocalServices.getService(PackageManagerInternal.class));
+        doReturn(mMockInputManagerInternal)
+                .when(() -> LocalServices.getService(InputManagerInternal.class));
+        doReturn(mMockDisplayManagerInternal)
+                .when(() -> LocalServices.getService(DisplayManagerInternal.class));
+        doReturn(mMockUserManagerInternal)
+                .when(() -> LocalServices.getService(UserManagerInternal.class));
+        doReturn(mMockIInputMethodManager)
+                .when(() -> ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
+        doReturn(mMockIPlatformCompat)
+                .when(() -> ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+
+        // Stubbing out context related methods to avoid the system holding strong references to
+        // InputMethodManagerService.
+        doNothing().when(mContext).enforceCallingPermission(anyString(), anyString());
+        doNothing().when(mContext).sendBroadcastAsUser(any(), any());
+        doReturn(null).when(mContext).registerReceiver(any(), any());
+        doReturn(null)
+                .when(mContext)
+                .registerReceiverAsUser(any(), any(), any(), anyString(), any(), anyInt());
+
+        // Injecting and mocked InputMethodBindingController and InputMethod.
+        mMockInputMethodInvoker = IInputMethodInvoker.create(mMockInputMethod);
+        InputManager.resetInstance(mMockIInputManager);
+        synchronized (ImfLock.class) {
+            when(mMockInputMethodBindingController.getCurMethod())
+                    .thenReturn(mMockInputMethodInvoker);
+            when(mMockInputMethodBindingController.bindCurrentMethod())
+                    .thenReturn(SUCCESS_WAITING_IME_BINDING_RESULT);
+            doNothing().when(mMockInputMethodBindingController).unbindCurrentMethod();
+            when(mMockInputMethodBindingController.getSelectedMethodId())
+                    .thenReturn(TEST_SELECTED_IME_ID);
+        }
+
+        // Shuffling around all other initialization to make the test runnable.
+        when(mMockIInputManager.getInputDeviceIds()).thenReturn(new int[0]);
+        when(mMockIInputMethodManager.isImeTraceEnabled()).thenReturn(false);
+        when(mMockIPlatformCompat.isChangeEnabledByUid(anyLong(), anyInt())).thenReturn(true);
+        when(mMockUserManagerInternal.isUserRunning(anyInt())).thenReturn(true);
+        when(mMockUserManagerInternal.getProfileIds(anyInt(), anyBoolean()))
+                .thenReturn(new int[] {0});
+        when(mMockActivityManagerInternal.isSystemReady()).thenReturn(true);
+        when(mMockPackageManagerInternal.getPackageUid(anyString(), anyLong(), anyInt()))
+                .thenReturn(Binder.getCallingUid());
+        when(mMockWindowManagerInternal.onToggleImeRequested(anyBoolean(), any(), any(), anyInt()))
+                .thenReturn(TEST_IME_TARGET_INFO);
+        when(mMockInputMethodClient.asBinder()).thenReturn(mMockInputMethodBinder);
+
+        // Used by lazy initializing draw IMS nav bar at InputMethodManagerService#systemRunning(),
+        // which is ok to be mocked out for now.
+        doReturn(null).when(() -> SystemServerInitThreadPool.submit(any(), anyString()));
+
+        mServiceThread =
+                new ServiceThread(
+                        "TestServiceThread",
+                        Process.THREAD_PRIORITY_FOREGROUND, /* allowIo */
+                        false);
+        mInputMethodManagerService =
+                new InputMethodManagerService(
+                        mContext, mServiceThread, mMockInputMethodBindingController);
+
+        // Start a InputMethodManagerService.Lifecycle to publish and manage the lifecycle of
+        // InputMethodManagerService, which is closer to the real situation.
+        InputMethodManagerService.Lifecycle lifecycle =
+                new InputMethodManagerService.Lifecycle(mContext, mInputMethodManagerService);
+
+        // Public local InputMethodManagerService.
+        lifecycle.onStart();
+        try {
+            // After this boot phase, services can broadcast Intents.
+            lifecycle.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+        } catch (SecurityException e) {
+            // Security exception to permission denial is expected in test, mocking out to ensure
+            // InputMethodManagerService as system ready state.
+            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
+                throw e;
+            }
+        }
+
+        // Call InputMethodManagerService#addClient() as a preparation to start interacting with it.
+        mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection, 0);
+    }
+
+    @After
+    public void tearDown() {
+        if (mServiceThread != null) {
+            mServiceThread.quitSafely();
+        }
+
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    protected void verifyShowSoftInput(boolean setVisible, boolean showSoftInput)
+            throws RemoteException {
+        synchronized (ImfLock.class) {
+            verify(mMockInputMethodBindingController, times(setVisible ? 1 : 0))
+                    .setCurrentMethodVisible();
+        }
+        verify(mMockInputMethod, times(showSoftInput ? 1 : 0))
+                .showSoftInput(any(), anyInt(), any());
+    }
+
+    protected void verifyHideSoftInput(boolean setNotVisible, boolean hideSoftInput)
+            throws RemoteException {
+        synchronized (ImfLock.class) {
+            verify(mMockInputMethodBindingController, times(setNotVisible ? 1 : 0))
+                    .setCurrentMethodNotVisible();
+        }
+        verify(mMockInputMethod, times(hideSoftInput ? 1 : 0))
+                .hideSoftInput(any(), anyInt(), any());
+    }
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
new file mode 100644
index 0000000..ffa2729
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
@@ -0,0 +1,266 @@
+/*
+ * 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.inputmethod;
+
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE;
+
+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.when;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.inputmethod.EditorInfo;
+import android.window.ImeOnBackInvokedDispatcher;
+
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test the behavior of {@link InputMethodManagerService#startInputOrWindowGainedFocus(int,
+ * IInputMethodClient, IBinder, int, int, int, EditorInfo, IRemoteInputConnection,
+ * IRemoteAccessibilityInputConnection, int, int, ImeOnBackInvokedDispatcher)}.
+ */
+@RunWith(Parameterized.class)
+public class InputMethodManagerServiceWindowGainedFocusTest
+        extends InputMethodManagerServiceTestBase {
+    private static final String TAG = "IMMSWindowGainedFocusTest";
+
+    private static final int[] SOFT_INPUT_STATE_FLAGS =
+            new int[] {
+                SOFT_INPUT_STATE_UNSPECIFIED,
+                SOFT_INPUT_STATE_UNCHANGED,
+                SOFT_INPUT_STATE_HIDDEN,
+                SOFT_INPUT_STATE_ALWAYS_HIDDEN,
+                SOFT_INPUT_STATE_VISIBLE,
+                SOFT_INPUT_STATE_ALWAYS_VISIBLE
+            };
+    private static final int[] SOFT_INPUT_ADJUST_FLAGS =
+            new int[] {
+                SOFT_INPUT_ADJUST_UNSPECIFIED,
+                SOFT_INPUT_ADJUST_RESIZE,
+                SOFT_INPUT_ADJUST_PAN,
+                SOFT_INPUT_ADJUST_NOTHING
+            };
+    private static final int DEFAULT_SOFT_INPUT_FLAG =
+            StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR;
+
+    @Parameterized.Parameters(name = "softInputState={0}, softInputAdjustment={1}")
+    public static List<Object[]> softInputModeConfigs() {
+        ArrayList<Object[]> params = new ArrayList<>();
+        for (int softInputState : SOFT_INPUT_STATE_FLAGS) {
+            for (int softInputAdjust : SOFT_INPUT_ADJUST_FLAGS) {
+                params.add(new Object[] {softInputState, softInputAdjust});
+            }
+        }
+        return params;
+    }
+
+    private final int mSoftInputState;
+    private final int mSoftInputAdjustment;
+
+    public InputMethodManagerServiceWindowGainedFocusTest(
+            int softInputState, int softInputAdjustment) {
+        mSoftInputState = softInputState;
+        mSoftInputAdjustment = softInputAdjustment;
+    }
+
+    @Test
+    public void startInputOrWindowGainedFocus_forwardNavigation() throws RemoteException {
+        mockHasImeFocusAndRestoreImeVisibility(false /* restoreImeVisibility */);
+
+        assertThat(
+                        startInputOrWindowGainedFocus(
+                                DEFAULT_SOFT_INPUT_FLAG, true /* forwardNavigation */))
+                .isEqualTo(SUCCESS_WAITING_IME_BINDING_RESULT);
+
+        switch (mSoftInputState) {
+            case SOFT_INPUT_STATE_UNSPECIFIED:
+                boolean showSoftInput = mSoftInputAdjustment == SOFT_INPUT_ADJUST_RESIZE;
+                verifyShowSoftInput(
+                        showSoftInput /* setVisible */, showSoftInput /* showSoftInput */);
+                // Soft input was hidden by default, so it doesn't need to call
+                // {@code IMS#hideSoftInput()}.
+                verifyHideSoftInput(!showSoftInput /* setNotVisible */, false /* hideSoftInput */);
+                break;
+            case SOFT_INPUT_STATE_VISIBLE:
+            case SOFT_INPUT_STATE_ALWAYS_VISIBLE:
+                verifyShowSoftInput(true /* setVisible */, true /* showSoftInput */);
+                verifyHideSoftInput(false /* setNotVisible */, false /* hideSoftInput */);
+                break;
+            case SOFT_INPUT_STATE_UNCHANGED: // Do nothing
+                verifyShowSoftInput(false /* setVisible */, false /* showSoftInput */);
+                verifyHideSoftInput(false /* setNotVisible */, false /* hideSoftInput */);
+                break;
+            case SOFT_INPUT_STATE_HIDDEN:
+            case SOFT_INPUT_STATE_ALWAYS_HIDDEN:
+                verifyShowSoftInput(false /* setVisible */, false /* showSoftInput */);
+                // Soft input was hidden by default, so it doesn't need to call
+                // {@code IMS#hideSoftInput()}.
+                verifyHideSoftInput(true /* setNotVisible */, false /* hideSoftInput */);
+                break;
+            default:
+                throw new IllegalStateException(
+                        "Unhandled soft input mode: "
+                                + InputMethodDebug.softInputModeToString(mSoftInputState));
+        }
+    }
+
+    @Test
+    public void startInputOrWindowGainedFocus_notForwardNavigation() throws RemoteException {
+        mockHasImeFocusAndRestoreImeVisibility(false /* restoreImeVisibility */);
+
+        assertThat(
+                        startInputOrWindowGainedFocus(
+                                DEFAULT_SOFT_INPUT_FLAG, false /* forwardNavigation */))
+                .isEqualTo(SUCCESS_WAITING_IME_BINDING_RESULT);
+
+        switch (mSoftInputState) {
+            case SOFT_INPUT_STATE_UNSPECIFIED:
+                boolean hideSoftInput = mSoftInputAdjustment != SOFT_INPUT_ADJUST_RESIZE;
+                verifyShowSoftInput(false /* setVisible */, false /* showSoftInput */);
+                // Soft input was hidden by default, so it doesn't need to call
+                // {@code IMS#hideSoftInput()}.
+                verifyHideSoftInput(hideSoftInput /* setNotVisible */, false /* hideSoftInput */);
+                break;
+            case SOFT_INPUT_STATE_VISIBLE:
+            case SOFT_INPUT_STATE_HIDDEN:
+            case SOFT_INPUT_STATE_UNCHANGED: // Do nothing
+                verifyShowSoftInput(false /* setVisible */, false /* showSoftInput */);
+                verifyHideSoftInput(false /* setNotVisible */, false /* hideSoftInput */);
+                break;
+            case SOFT_INPUT_STATE_ALWAYS_VISIBLE:
+                verifyShowSoftInput(true /* setVisible */, true /* showSoftInput */);
+                verifyHideSoftInput(false /* setNotVisible */, false /* hideSoftInput */);
+                break;
+            case SOFT_INPUT_STATE_ALWAYS_HIDDEN:
+                verifyShowSoftInput(false /* setVisible */, false /* showSoftInput */);
+                // Soft input was hidden by default, so it doesn't need to call
+                // {@code IMS#hideSoftInput()}.
+                verifyHideSoftInput(true /* setNotVisible */, false /* hideSoftInput */);
+                break;
+            default:
+                throw new IllegalStateException(
+                        "Unhandled soft input mode: "
+                                + InputMethodDebug.softInputModeToString(mSoftInputState));
+        }
+    }
+
+    @Test
+    public void startInputOrWindowGainedFocus_userNotRunning() throws RemoteException {
+        when(mMockUserManagerInternal.isUserRunning(anyInt())).thenReturn(false);
+
+        assertThat(
+                        startInputOrWindowGainedFocus(
+                                DEFAULT_SOFT_INPUT_FLAG, true /* forwardNavigation */))
+                .isEqualTo(InputBindResult.INVALID_USER);
+        verifyShowSoftInput(false /* setVisible */, false /* showSoftInput */);
+        verifyHideSoftInput(false /* setNotVisible */, false /* hideSoftInput */);
+    }
+
+    @Test
+    public void startInputOrWindowGainedFocus_invalidFocusStatus() throws RemoteException {
+        int[] invalidImeClientFocus =
+                new int[] {
+                    WindowManagerInternal.ImeClientFocusResult.NOT_IME_TARGET_WINDOW,
+                    WindowManagerInternal.ImeClientFocusResult.DISPLAY_ID_MISMATCH,
+                    WindowManagerInternal.ImeClientFocusResult.INVALID_DISPLAY_ID
+                };
+        InputBindResult[] inputBingResult =
+                new InputBindResult[] {
+                    InputBindResult.NOT_IME_TARGET_WINDOW,
+                    InputBindResult.DISPLAY_ID_MISMATCH,
+                    InputBindResult.INVALID_DISPLAY_ID
+                };
+
+        for (int i = 0; i < invalidImeClientFocus.length; i++) {
+            when(mMockWindowManagerInternal.hasInputMethodClientFocus(
+                            any(), anyInt(), anyInt(), anyInt()))
+                    .thenReturn(invalidImeClientFocus[i]);
+
+            assertThat(
+                            startInputOrWindowGainedFocus(
+                                    DEFAULT_SOFT_INPUT_FLAG, true /* forwardNavigation */))
+                    .isEqualTo(inputBingResult[i]);
+            verifyShowSoftInput(false /* setVisible */, false /* showSoftInput */);
+            verifyHideSoftInput(false /* setNotVisible */, false /* hideSoftInput */);
+        }
+    }
+
+    private InputBindResult startInputOrWindowGainedFocus(
+            int startInputFlag, boolean forwardNavigation) {
+        int softInputMode = mSoftInputState | mSoftInputAdjustment;
+        if (forwardNavigation) {
+            softInputMode |= SOFT_INPUT_IS_FORWARD_NAVIGATION;
+        }
+
+        Log.i(
+                TAG,
+                "startInputOrWindowGainedFocus() softInputStateFlag="
+                        + InputMethodDebug.softInputModeToString(mSoftInputState)
+                        + ", softInputAdjustFlag="
+                        + InputMethodDebug.softInputModeToString(mSoftInputAdjustment));
+
+        return mInputMethodManagerService.startInputOrWindowGainedFocus(
+                StartInputReason.WINDOW_FOCUS_GAIN /* startInputReason */,
+                mMockInputMethodClient /* client */,
+                mWindowToken /* windowToken */,
+                startInputFlag /* startInputFlags */,
+                softInputMode /* softInputMode */,
+                0 /* windowFlags */,
+                mEditorInfo /* editorInfo */,
+                mMockRemoteInputConnection /* inputConnection */,
+                mMockRemoteAccessibilityInputConnection /* remoteAccessibilityInputConnection */,
+                mTargetSdkVersion /* unverifiedTargetSdkVersion */,
+                mCallingUserId /* userId */,
+                mMockImeOnBackInvokedDispatcher /* imeDispatcher */);
+    }
+
+    private void mockHasImeFocusAndRestoreImeVisibility(boolean restoreImeVisibility) {
+        when(mMockWindowManagerInternal.hasInputMethodClientFocus(
+                        any(), anyInt(), anyInt(), anyInt()))
+                .thenReturn(WindowManagerInternal.ImeClientFocusResult.HAS_IME_FOCUS);
+        when(mMockWindowManagerInternal.shouldRestoreImeVisibility(any()))
+                .thenReturn(restoreImeVisibility);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index cb14864..2583f44 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -325,7 +325,7 @@
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any());
         doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), anyString(), any(), any());
         doNothing().when(mAlarmManager)
-                .setWindow(anyInt(), anyLong(), anyLong(), anyString(), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), anyString(), any(), any(Handler.class));
         doReturn(mock(Sensor.class)).when(mSensorManager)
                 .getDefaultSensor(eq(Sensor.TYPE_SIGNIFICANT_MOTION), eq(true));
         doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
@@ -1111,12 +1111,12 @@
         alarmManagerInOrder.verify(mAlarmManager).setWindow(
                 eq(AlarmManager.ELAPSED_REALTIME),
                 eq(idleAfterInactiveExpiryTime),
-                anyLong(), anyString(), any(), any());
+                anyLong(), anyString(), any(), any(Handler.class));
         // Maintenance alarm
         alarmManagerInOrder.verify(mAlarmManager).setWindow(
                 eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
                 eq(idleAfterInactiveExpiryTime + idlingTimeMs),
-                anyLong(), anyString(), any(), any());
+                anyLong(), anyString(), any(), any(Handler.class));
 
         final AlarmManager.OnAlarmListener progressionListener =
                 alarmListenerCaptor.getAllValues().get(0);
@@ -1130,7 +1130,7 @@
         alarmManagerInOrder.verify(mAlarmManager).setWindow(
                 eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
                 eq(mInjector.nowElapsed + idlingTimeMs),
-                anyLong(), anyString(), any(), any());
+                anyLong(), anyString(), any(), any(Handler.class));
 
         for (int i = 0; i < 2; ++i) {
             // IDLE->MAINTENANCE alarm
@@ -1144,12 +1144,12 @@
             alarmManagerInOrder.verify(mAlarmManager).setWindow(
                     eq(AlarmManager.ELAPSED_REALTIME),
                     eq(maintenanceExpiryTime),
-                    anyLong(), anyString(), any(), any());
+                    anyLong(), anyString(), any(), any(Handler.class));
             // Set IDLE->MAINTENANCE
             alarmManagerInOrder.verify(mAlarmManager).setWindow(
                     eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
                     eq(maintenanceExpiryTime + idlingTimeMs),
-                    anyLong(), anyString(), any(), any());
+                    anyLong(), anyString(), any(), any(Handler.class));
 
             // MAINTENANCE->IDLE alarm
             mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
@@ -1159,7 +1159,7 @@
             alarmManagerInOrder.verify(mAlarmManager).setWindow(
                     eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
                     eq(mInjector.nowElapsed + idlingTimeMs),
-                    anyLong(), anyString(), any(), any());
+                    anyLong(), anyString(), any(), any(Handler.class));
         }
     }
 
@@ -2019,7 +2019,8 @@
         final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
                 .forClass(AlarmManager.OnAlarmListener.class);
         doNothing().when(mAlarmManager).setWindow(
-                anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
+                anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"), any(),
+                any(Handler.class));
         doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
                 eq("DeviceIdleController.motion_registration"),
                 alarmListener.capture(), any());
@@ -2063,7 +2064,8 @@
         final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
                 .forClass(AlarmManager.OnAlarmListener.class);
         doNothing().when(mAlarmManager).setWindow(
-                anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
+                anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"), any(),
+                any(Handler.class));
         doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
                 eq("DeviceIdleController.motion_registration"),
                 alarmListener.capture(), any());
@@ -2130,7 +2132,7 @@
                         eq(SensorManager.SENSOR_DELAY_NORMAL));
         inOrder.verify(mAlarmManager).setWindow(
                 anyInt(), eq(mInjector.nowElapsed + mConstants.MOTION_INACTIVE_TIMEOUT), anyLong(),
-                eq("DeviceIdleController.motion"), any(), any());
+                eq("DeviceIdleController.motion"), any(), any(Handler.class));
         final SensorEventListener listener = listenerCaptor.getValue();
 
         // Trigger motion
@@ -2140,7 +2142,7 @@
         final ArgumentCaptor<Long> registrationTimeCaptor = ArgumentCaptor.forClass(Long.class);
         inOrder.verify(mAlarmManager).setWindow(
                 anyInt(), registrationTimeCaptor.capture(), anyLong(),
-                eq("DeviceIdleController.motion_registration"), any(), any());
+                eq("DeviceIdleController.motion_registration"), any(), any(Handler.class));
 
         // Make sure the listener is re-registered.
         mInjector.nowElapsed = registrationTimeCaptor.getValue();
@@ -2150,7 +2152,7 @@
                         eq(SensorManager.SENSOR_DELAY_NORMAL));
         final ArgumentCaptor<Long> timeoutCaptor = ArgumentCaptor.forClass(Long.class);
         inOrder.verify(mAlarmManager).setWindow(anyInt(), timeoutCaptor.capture(), anyLong(),
-                eq("DeviceIdleController.motion"), any(), any());
+                eq("DeviceIdleController.motion"), any(), any(Handler.class));
 
         // No motion before timeout
         stationaryListener.motionExpected = false;
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
index f46877e..2547347 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
@@ -16,10 +16,19 @@
 
 package com.android.server.job;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_EJ;
 import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_REGULAR;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER_IMPORTANT;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_FGS;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -47,15 +56,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
-import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
-import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER_IMPORTANT;
-import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
-import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_FGS;
-import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
-import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
-
 import com.android.internal.R;
 import com.android.internal.app.IBatteryStats;
 import com.android.server.LocalServices;
@@ -73,6 +73,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -124,6 +125,7 @@
         mMockingSession = mockitoSession()
                 .initMocks(this)
                 .mockStatic(AppGlobals.class)
+                .spyStatic(DeviceConfig.class)
                 .strictness(Strictness.LENIENT)
                 .startMocking();
         final JobSchedulerService jobSchedulerService = mock(JobSchedulerService.class);
@@ -134,6 +136,8 @@
         when(mContext.getResources()).thenReturn(mResources);
         doReturn(mContext).when(jobSchedulerService).getTestableContext();
         mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
+        doAnswer((Answer<DeviceConfig.Properties>) invocationOnMock -> mConfigBuilder.build())
+                .when(() -> DeviceConfig.getProperties(eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER)));
         mPendingJobQueue = new PendingJobQueue();
         doReturn(mPendingJobQueue).when(jobSchedulerService).getPendingJobQueue();
         doReturn(mIPackageManager).when(AppGlobals::getPackageManager);
@@ -595,7 +599,6 @@
     }
 
     private void updateDeviceConfig() throws Exception {
-        DeviceConfig.setProperties(mConfigBuilder.build());
         mJobConcurrencyManager.updateConfigLocked();
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 1753fc7..fc737d0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -41,6 +41,7 @@
 import android.app.IActivityManager;
 import android.app.UiModeManager;
 import android.app.job.JobInfo;
+import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
@@ -235,6 +236,59 @@
                 mService.getMinJobExecutionGuaranteeMs(jobDef));
     }
 
+
+    /**
+     * Confirm that {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int)}
+     * returns a job with the correct delay and deadline constraints.
+     */
+    @Test
+    public void testGetRescheduleJobForFailure() {
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        final long initialBackoffMs = MINUTE_IN_MILLIS;
+        mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO = 3;
+
+        JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure",
+                createJobInfo()
+                        .setBackoffCriteria(initialBackoffMs, JobInfo.BACKOFF_POLICY_LINEAR));
+        assertEquals(JobStatus.NO_EARLIEST_RUNTIME, originalJob.getEarliestRunTime());
+        assertEquals(JobStatus.NO_LATEST_RUNTIME, originalJob.getLatestRunTimeElapsed());
+
+        // failure = 0, systemStop = 1
+        JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob,
+                JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL);
+        assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime());
+        assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+
+        // failure = 0, systemStop = 2
+        rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+                JobParameters.INTERNAL_STOP_REASON_PREEMPT);
+        // failure = 0, systemStop = 3
+        rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+                JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED);
+        assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime());
+        assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+
+        // failure = 0, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO
+        for (int i = 0; i < mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; ++i) {
+            rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+                    JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED);
+        }
+        assertEquals(nowElapsed + 2 * initialBackoffMs, rescheduledJob.getEarliestRunTime());
+        assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+
+        // failure = 1, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO
+        rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+                JobParameters.INTERNAL_STOP_REASON_TIMEOUT);
+        assertEquals(nowElapsed + 3 * initialBackoffMs, rescheduledJob.getEarliestRunTime());
+        assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+
+        // failure = 2, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO
+        rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
+                JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
+        assertEquals(nowElapsed + 4 * initialBackoffMs, rescheduledJob.getEarliestRunTime());
+        assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
+    }
+
     /**
      * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
      * with the correct delay and deadline constraints if the periodic job is scheduled with the
@@ -544,14 +598,16 @@
         final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
         JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_failedJob",
                 createJobInfo().setPeriodic(HOUR_IN_MILLIS));
-        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
+                JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
 
         JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
         assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
         assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
 
         advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 5 minutes
-        failedJob = mService.getRescheduleJobForFailureLocked(job);
+        failedJob = mService.getRescheduleJobForFailureLocked(job,
+                JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
         advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 10 minutes
 
         rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
@@ -559,7 +615,8 @@
         assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
 
         advanceElapsedClock(35 * MINUTE_IN_MILLIS); // now + 45 minutes
-        failedJob = mService.getRescheduleJobForFailureLocked(job);
+        failedJob = mService.getRescheduleJobForFailureLocked(job,
+                JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
         advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes
 
         rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
@@ -569,7 +626,8 @@
         assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
 
         advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes
-        failedJob = mService.getRescheduleJobForFailureLocked(job);
+        failedJob = mService.getRescheduleJobForFailureLocked(job,
+                JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
         advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes
 
         rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
@@ -665,7 +723,8 @@
     public void testGetRescheduleJobForPeriodic_outsideWindow_failedJob() {
         JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_failedJob",
                 createJobInfo().setPeriodic(HOUR_IN_MILLIS));
-        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
+                JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
         long now = sElapsedRealtimeClock.millis();
         long nextWindowStartTime = now + HOUR_IN_MILLIS;
         long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
@@ -701,7 +760,8 @@
         JobStatus job = createJobStatus(
                 "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob",
                 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS));
-        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
+                JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
         // First window starts 30 minutes from now.
         advanceElapsedClock(30 * MINUTE_IN_MILLIS);
         long now = sElapsedRealtimeClock.millis();
@@ -742,7 +802,8 @@
         JobStatus job = createJobStatus(
                 "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod",
                 createJobInfo().setPeriodic(7 * DAY_IN_MILLIS, 9 * HOUR_IN_MILLIS));
-        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
+                JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
         // First window starts 6.625 days from now.
         advanceElapsedClock(6 * DAY_IN_MILLIS + 15 * HOUR_IN_MILLIS);
         long now = sElapsedRealtimeClock.millis();
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
index 59cb43f..7c435be 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
@@ -432,10 +432,10 @@
         assertFalse(topStartedJobs.contains(unrelatedJob));
 
         // Job cleanup
-        mBatteryController.maybeStopTrackingJobLocked(batteryJob, null, false);
-        mBatteryController.maybeStopTrackingJobLocked(chargingJob, null, false);
-        mBatteryController.maybeStopTrackingJobLocked(bothPowerJob, null, false);
-        mBatteryController.maybeStopTrackingJobLocked(unrelatedJob, null, false);
+        mBatteryController.maybeStopTrackingJobLocked(batteryJob, null);
+        mBatteryController.maybeStopTrackingJobLocked(chargingJob, null);
+        mBatteryController.maybeStopTrackingJobLocked(bothPowerJob, null);
+        mBatteryController.maybeStopTrackingJobLocked(unrelatedJob, null);
         assertFalse(trackedJobs.contains(batteryJob));
         assertFalse(trackedJobs.contains(chargingJob));
         assertFalse(trackedJobs.contains(bothPowerJob));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index 674e500..3bee687 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -489,19 +489,22 @@
         JobInfo.Builder jb = createJob(0).setOverrideDeadline(1000L);
         JobStatus js = createJobStatus("time", jb);
         js = new JobStatus(
-                js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, FROZEN_TIME, FROZEN_TIME);
+                js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, /* numSystemStops */ 0,
+                FROZEN_TIME, FROZEN_TIME);
 
         assertEquals(mFcConfig.RESCHEDULED_JOB_DEADLINE_MS,
                 mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
 
         js = new JobStatus(
-                js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 3, FROZEN_TIME, FROZEN_TIME);
+                js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, /* numSystemStops */ 1,
+                FROZEN_TIME, FROZEN_TIME);
 
         assertEquals(2 * mFcConfig.RESCHEDULED_JOB_DEADLINE_MS,
                 mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
 
         js = new JobStatus(
-                js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 10, FROZEN_TIME, FROZEN_TIME);
+                js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, /* numSystemStops */ 10,
+                FROZEN_TIME, FROZEN_TIME);
         assertEquals(mFcConfig.MAX_RESCHEDULED_DEADLINE_MS,
                 mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
     }
@@ -637,7 +640,12 @@
         JobInfo.Builder jb = createJob(0);
         JobStatus js = createJobStatus("time", jb);
         js = new JobStatus(
-                js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, FROZEN_TIME, FROZEN_TIME);
+                js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, /* numSystemStops */ 0,
+                FROZEN_TIME, FROZEN_TIME);
+        assertFalse(js.hasFlexibilityConstraint());
+        js = new JobStatus(
+                js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, /* numSystemStops */ 1,
+                FROZEN_TIME, FROZEN_TIME);
         assertFalse(js.hasFlexibilityConstraint());
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 149ae0b..7f522b0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -248,20 +248,32 @@
 
         // Less than 2 failures, priority shouldn't be affected.
         assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
-        int backoffAttempt = 1;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        int numFailures = 1;
+        int numSystemStops = 0;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
 
         // 2+ failures, priority should be lowered as much as possible.
-        backoffAttempt = 2;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 2;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
-        backoffAttempt = 5;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 5;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
-        backoffAttempt = 8;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 8;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
+
+        // System stops shouldn't factor in the downgrade.
+        numSystemStops = 10;
+        numFailures = 0;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
+        assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
     }
 
     @Test
@@ -274,33 +286,48 @@
 
         // Less than 2 failures, priority shouldn't be affected.
         assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
-        int backoffAttempt = 1;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        int numFailures = 1;
+        int numSystemStops = 0;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
 
         // Failures in [2,4), priority should be lowered slightly.
-        backoffAttempt = 2;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 2;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority());
-        backoffAttempt = 3;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 3;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority());
 
         // Failures in [4,6), priority should be lowered more.
-        backoffAttempt = 4;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 4;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
-        backoffAttempt = 5;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 5;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
 
         // 6+ failures, priority should be lowered as much as possible.
-        backoffAttempt = 6;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 6;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
-        backoffAttempt = 12;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 12;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
+
+        // System stops shouldn't factor in the downgrade.
+        numSystemStops = 10;
+        numFailures = 0;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
+        assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
     }
 
     /**
@@ -317,23 +344,36 @@
 
         // Less than 6 failures, priority shouldn't be affected.
         assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
-        int backoffAttempt = 1;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        int numFailures = 1;
+        int numSystemStops = 0;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
-        backoffAttempt = 4;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 4;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
-        backoffAttempt = 5;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 5;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
 
         // 6+ failures, priority should be lowered as much as possible.
-        backoffAttempt = 6;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 6;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
-        backoffAttempt = 12;
-        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+        numFailures = 12;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
         assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
+
+        // System stops shouldn't factor in the downgrade.
+        numSystemStops = 10;
+        numFailures = 0;
+        job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
+                numSystemStops, 0, 0);
+        assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
     }
 
     /**
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index bb477b1..b949b3b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -47,6 +47,7 @@
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Handler;
 import android.os.Looper;
 import android.os.Process;
 import android.os.SystemClock;
@@ -276,13 +277,13 @@
         inOrder.verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1))
                 .setWindow(
                         anyInt(), eq(sElapsedRealtimeClock.millis() + 4 * HOUR_IN_MILLIS),
-                        anyLong(), eq(TAG_PREFETCH), any(), any());
+                        anyLong(), eq(TAG_PREFETCH), any(), any(Handler.class));
 
         setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 3 * HOUR_IN_MILLIS);
         inOrder.verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1))
                 .setWindow(
                         anyInt(), eq(sElapsedRealtimeClock.millis() + 8 * HOUR_IN_MILLIS),
-                        anyLong(), eq(TAG_PREFETCH), any(), any());
+                        anyLong(), eq(TAG_PREFETCH), any(), any(Handler.class));
     }
 
     @Test
@@ -414,7 +415,7 @@
         verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1))
                 .setWindow(
                         anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
-                        anyLong(), eq(TAG_PREFETCH), any(), any());
+                        anyLong(), eq(TAG_PREFETCH), any(), any(Handler.class));
         assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
         assertFalse(jobStatus.isReady());
     }
@@ -464,7 +465,7 @@
         verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1))
                 .setWindow(
                         anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
-                        anyLong(), eq(TAG_PREFETCH), any(), any());
+                        anyLong(), eq(TAG_PREFETCH), any(), any(Handler.class));
         assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
         assertFalse(jobStatus.isReady());
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 9407968..17822c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1356,7 +1356,7 @@
         synchronized (mQuotaController.mLock) {
             assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                     mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
-            mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
         }
 
         setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
@@ -1441,7 +1441,7 @@
         synchronized (mQuotaController.mLock) {
             assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
-            mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
         }
 
         setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
@@ -1474,7 +1474,7 @@
         synchronized (mQuotaController.mLock) {
             assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
-            mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
         }
 
         setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
@@ -2017,7 +2017,7 @@
             setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
         }
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
 
         advanceElapsedClock(15 * SECOND_IN_MILLIS);
@@ -2032,7 +2032,7 @@
             setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
         }
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
 
         advanceElapsedClock(10 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS);
@@ -2090,7 +2090,7 @@
             setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid);
         }
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null);
         }
 
         advanceElapsedClock(15 * SECOND_IN_MILLIS);
@@ -2105,9 +2105,9 @@
             setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid);
         }
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null);
 
-            mQuotaController.maybeStopTrackingJobLocked(unaffected, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(unaffected, null);
 
             assertTrue(mQuotaController.isWithinQuotaLocked(unaffected));
             assertTrue(unaffected.isReady());
@@ -2226,8 +2226,8 @@
 
         advanceElapsedClock(MINUTE_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, null, false);
-            mQuotaController.maybeStopTrackingJobLocked(job2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job1, null);
+            mQuotaController.maybeStopTrackingJobLocked(job2, null);
             assertFalse(mQuotaController
                     .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
             assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
@@ -2312,8 +2312,8 @@
 
         advanceElapsedClock(MINUTE_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, null, false);
-            mQuotaController.maybeStopTrackingJobLocked(job2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job1, null);
+            mQuotaController.maybeStopTrackingJobLocked(job2, null);
             assertFalse(mQuotaController
                     .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
             assertEquals(12, stats.jobCountLimit);
@@ -2390,7 +2390,7 @@
         advanceElapsedClock(MINUTE_IN_MILLIS);
         mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job1, null);
             assertTrue(mQuotaController
                     .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
             assertEquals(4, stats.sessionCountLimit);
@@ -2404,7 +2404,7 @@
 
         advanceElapsedClock(MINUTE_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job2, null);
             assertFalse(mQuotaController
                     .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
             assertEquals(4, stats.sessionCountLimit);
@@ -2708,7 +2708,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         // Test with timing sessions out of window but still under max execution limit.
@@ -2725,7 +2725,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(now - 2 * HOUR_IN_MILLIS, 55 * MINUTE_IN_MILLIS, 1), false);
@@ -2734,7 +2734,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         synchronized (mQuotaController.mLock) {
             mQuotaController.prepareForExecutionLocked(jobStatus);
@@ -2749,7 +2749,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
     }
 
     @Test
@@ -2771,7 +2772,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions out of window.
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
@@ -2782,7 +2783,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions in window but still in quota.
         final long end = now - (2 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
@@ -2796,7 +2797,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Add some more sessions, but still in quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2808,7 +2809,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test when out of quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2818,7 +2819,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
 
         // Alarm already scheduled, so make sure it's not scheduled again.
         synchronized (mQuotaController.mLock) {
@@ -2826,7 +2828,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
     }
 
     @Test
@@ -2850,7 +2853,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions out of window.
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
@@ -2861,7 +2864,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions in window but still in quota.
         final long start = now - (6 * HOUR_IN_MILLIS);
@@ -2873,7 +2876,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Add some more sessions, but still in quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2885,7 +2888,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test when out of quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2895,7 +2898,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
 
         // Alarm already scheduled, so make sure it's not scheduled again.
         synchronized (mQuotaController.mLock) {
@@ -2903,7 +2907,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
     }
 
     /**
@@ -2932,7 +2937,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions out of window.
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
@@ -2943,7 +2948,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions in window but still in quota.
         final long start = now - (6 * HOUR_IN_MILLIS);
@@ -2955,7 +2960,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Add some more sessions, but still in quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2967,7 +2972,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test when out of quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2977,7 +2982,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
 
         // Alarm already scheduled, so make sure it's not scheduled again.
         synchronized (mQuotaController.mLock) {
@@ -2985,7 +2991,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
     }
 
     @Test
@@ -3013,7 +3020,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions out of window.
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
@@ -3023,7 +3030,7 @@
             mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions in window but still in quota.
         final long start = now - (6 * HOUR_IN_MILLIS);
@@ -3039,7 +3046,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Add some more sessions, but still in quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -3051,7 +3058,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test when out of quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -3061,7 +3068,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
 
         // Alarm already scheduled, so make sure it's not scheduled again.
         synchronized (mQuotaController.mLock) {
@@ -3069,7 +3077,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
     }
 
     /** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */
@@ -3108,7 +3117,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
                 .cancel(any(AlarmManager.OnAlarmListener.class));
 
@@ -3123,7 +3133,7 @@
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         final long expectedFrequentAlarmTime =
                 outOfQuotaTime + (8 * HOUR_IN_MILLIS)
@@ -3135,7 +3145,7 @@
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         final long expectedRareAlarmTime =
                 outOfQuotaTime + (24 * HOUR_IN_MILLIS)
@@ -3146,7 +3156,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedRareAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedRareAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
 
         // And back up again.
         setStandbyBucket(FREQUENT_INDEX, jobStatus);
@@ -3156,7 +3167,7 @@
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         setStandbyBucket(WORKING_INDEX, jobStatus);
         synchronized (mQuotaController.mLock) {
@@ -3165,7 +3176,7 @@
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         setStandbyBucket(ACTIVE_INDEX, jobStatus);
         synchronized (mQuotaController.mLock) {
@@ -3173,7 +3184,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .cancel(any(AlarmManager.OnAlarmListener.class));
     }
@@ -3210,7 +3222,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Valid time in the future, so the count should be used.
         stats.jobRateLimitExpirationTimeElapsed = now + 5 * MINUTE_IN_MILLIS / 2;
@@ -3221,7 +3233,7 @@
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
     }
 
     /**
@@ -3318,7 +3330,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
     }
 
     private void runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck() {
@@ -3355,7 +3368,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
     }
 
     @Test
@@ -3711,7 +3725,7 @@
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
@@ -3737,7 +3751,7 @@
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -3766,7 +3780,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
@@ -3774,11 +3788,11 @@
         }
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null);
         }
         expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -3819,7 +3833,7 @@
         long start = JobSchedulerService.sElapsedRealtimeClock.millis();
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -3850,18 +3864,18 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         setDischarging();
         start = JobSchedulerService.sElapsedRealtimeClock.millis();
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -3879,7 +3893,7 @@
         setCharging();
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null);
         }
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
@@ -3905,7 +3919,7 @@
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -3934,7 +3948,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
@@ -3942,11 +3956,11 @@
         }
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null);
         }
         expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -3973,7 +3987,7 @@
         setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
@@ -4005,7 +4019,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -4030,11 +4044,11 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobFg3, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
         }
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
@@ -4063,7 +4077,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
         start = JobSchedulerService.sElapsedRealtimeClock.millis();
@@ -4073,11 +4087,11 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobFg3, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
         }
         expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -4116,11 +4130,11 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobFg1, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobFg2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobFg2, null);
         }
         assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
@@ -4155,11 +4169,11 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg1, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
         }
 
         assertEquals(2, stats.jobCountInRateLimitingWindow);
@@ -4193,7 +4207,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -4218,11 +4232,11 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobTop, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
         }
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
@@ -4253,7 +4267,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
@@ -4270,12 +4284,12 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobTop, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
-            mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
+            mQuotaController.maybeStopTrackingJobLocked(jobFg1, null);
         }
         expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -4312,7 +4326,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, job1, true);
+            mQuotaController.maybeStopTrackingJobLocked(job1, job1);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -4329,7 +4343,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job2, null);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -4348,7 +4362,7 @@
         long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS;
         advanceElapsedClock(elapsedGracePeriodMs);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job3, null);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS + elapsedGracePeriodMs, 1));
         assertEquals(expected,
@@ -4373,7 +4387,7 @@
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         expected.add(createTimingSession(start, remainingGracePeriod + 10 * SECOND_IN_MILLIS, 1));
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job4, job4, true);
+            mQuotaController.maybeStopTrackingJobLocked(job4, job4);
         }
         assertEquals(expected,
                 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -4387,7 +4401,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job5, job5, true);
+            mQuotaController.maybeStopTrackingJobLocked(job5, job5);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -4611,7 +4625,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Ran jobs up to the job limit. All of them should be allowed to run.
         for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; ++i) {
@@ -4624,13 +4638,13 @@
             }
             advanceElapsedClock(SECOND_IN_MILLIS);
             synchronized (mQuotaController.mLock) {
-                mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+                mQuotaController.maybeStopTrackingJobLocked(job, null);
             }
             advanceElapsedClock(SECOND_IN_MILLIS);
         }
         // Start alarm shouldn't have been scheduled since the app was in quota up until this point.
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // The app is now out of job count quota
         JobStatus throttledJob = createJobStatus(
@@ -4649,7 +4663,7 @@
         final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed;
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
     }
 
     /**
@@ -4680,7 +4694,7 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Ran jobs up to the job limit. All of them should be allowed to run.
         for (int i = 0; i < mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW; ++i) {
@@ -4695,13 +4709,13 @@
             }
             advanceElapsedClock(SECOND_IN_MILLIS);
             synchronized (mQuotaController.mLock) {
-                mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+                mQuotaController.maybeStopTrackingJobLocked(job, null);
             }
             advanceElapsedClock(SECOND_IN_MILLIS);
         }
         // Start alarm shouldn't have been scheduled since the app was in quota up until this point.
         verify(mAlarmManager, timeout(1000).times(0)).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // The app is now out of session count quota
         JobStatus throttledJob = createJobStatus(
@@ -4721,7 +4735,7 @@
         final long expectedWorkingAlarmTime = stats.sessionRateLimitExpirationTimeElapsed;
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
     }
 
     @Test
@@ -5185,7 +5199,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
 
         // Test with timing sessions out of window.
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
@@ -5196,7 +5211,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
 
         // Test with timing sessions in window but still in quota.
         final long end = now - (22 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
@@ -5208,7 +5224,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
 
         // Add some more sessions, but still in quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -5220,7 +5237,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
 
         // Test when out of quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -5230,7 +5248,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
 
         // Alarm already scheduled, so make sure it's not scheduled again.
         synchronized (mQuotaController.mLock) {
@@ -5238,7 +5257,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
     }
 
     /** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */
@@ -5282,7 +5302,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
                 .cancel(any(AlarmManager.OnAlarmListener.class));
 
@@ -5297,7 +5318,7 @@
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         setStandbyBucket(FREQUENT_INDEX);
         final long expectedFrequentAlarmTime =
@@ -5308,7 +5329,7 @@
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         setStandbyBucket(RARE_INDEX);
         final long expectedRareAlarmTime =
@@ -5319,7 +5340,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedRareAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedRareAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
 
         // And back up again.
         setStandbyBucket(FREQUENT_INDEX);
@@ -5329,7 +5351,7 @@
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         setStandbyBucket(WORKING_INDEX);
         synchronized (mQuotaController.mLock) {
@@ -5338,7 +5360,7 @@
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
-                eq(TAG_QUOTA_CHECK), any(), any());
+                eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         setStandbyBucket(ACTIVE_INDEX);
         synchronized (mQuotaController.mLock) {
@@ -5346,7 +5368,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX);
         }
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .cancel(any(AlarmManager.OnAlarmListener.class));
     }
@@ -5388,7 +5411,8 @@
                     SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
         }
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
     }
 
     /** Tests that TimingSessions aren't saved when the device is charging. */
@@ -5408,7 +5432,7 @@
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
@@ -5434,7 +5458,7 @@
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -5464,7 +5488,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
@@ -5472,11 +5496,11 @@
         }
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null);
         }
         expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
         assertEquals(expected,
@@ -5521,7 +5545,7 @@
         long start = JobSchedulerService.sElapsedRealtimeClock.millis();
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -5553,18 +5577,18 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         setDischarging();
         start = JobSchedulerService.sElapsedRealtimeClock.millis();
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -5583,7 +5607,7 @@
         setCharging();
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null);
         }
         assertEquals(expected,
                 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -5610,7 +5634,7 @@
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -5640,7 +5664,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
@@ -5648,11 +5672,11 @@
         }
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null);
         }
         expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
         assertEquals(expected,
@@ -5680,7 +5704,7 @@
         setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
         }
         assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
@@ -5715,7 +5739,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -5741,11 +5765,11 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobFg3, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
         }
         assertEquals(expected,
                 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -5775,7 +5799,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
         start = JobSchedulerService.sElapsedRealtimeClock.millis();
@@ -5785,11 +5809,11 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobFg3, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
         }
         expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
         assertEquals(expected,
@@ -5825,7 +5849,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -5851,11 +5875,11 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobTop, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
         }
         assertEquals(expected,
                 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -5887,7 +5911,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
@@ -5904,12 +5928,12 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobTop, null);
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
-            mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
+            mQuotaController.maybeStopTrackingJobLocked(jobFg1, null);
         }
         expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
         assertEquals(expected,
@@ -5946,7 +5970,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, job1, true);
+            mQuotaController.maybeStopTrackingJobLocked(job1, job1);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -5963,7 +5987,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job2, null);
         }
         assertEquals(expected,
                 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -5981,7 +6005,7 @@
         long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS;
         advanceElapsedClock(elapsedGracePeriodMs);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job3, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job3, null);
         }
         assertEquals(expected,
                 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -6005,7 +6029,7 @@
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job4, job4, true);
+            mQuotaController.maybeStopTrackingJobLocked(job4, job4);
         }
         assertEquals(expected,
                 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -6019,7 +6043,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job5, job5, true);
+            mQuotaController.maybeStopTrackingJobLocked(job5, job5);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -6049,7 +6073,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job, job, true);
+            mQuotaController.maybeStopTrackingJobLocked(job, job);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -6066,7 +6090,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -6085,7 +6109,7 @@
         long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS;
         advanceElapsedClock(elapsedGracePeriodMs);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
         }
         expected.add(createTimingSession(start, 12 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -6110,7 +6134,7 @@
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS + remainingGracePeriod, 1));
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job, job, true);
+            mQuotaController.maybeStopTrackingJobLocked(job, job);
         }
         assertEquals(expected,
                 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -6124,7 +6148,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job, job, true);
+            mQuotaController.maybeStopTrackingJobLocked(job, job);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -6169,7 +6193,7 @@
         // Wait for the grace period to expire so the handler can process the message.
         Thread.sleep(gracePeriodMs);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, job1, true);
+            mQuotaController.maybeStopTrackingJobLocked(job1, job1);
         }
         assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
@@ -6189,7 +6213,7 @@
         // Wait for the grace period to expire so the handler can process the message.
         Thread.sleep(gracePeriodMs);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(job2, null);
         }
         assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
@@ -6215,7 +6239,7 @@
         Thread.sleep(2 * gracePeriodMs);
         advanceElapsedClock(gracePeriodMs);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job3, job3, true);
+            mQuotaController.maybeStopTrackingJobLocked(job3, job3);
         }
         expected.add(createTimingSession(start, gracePeriodMs, 1));
         assertEquals(expected,
@@ -6243,7 +6267,7 @@
         Thread.sleep(2 * gracePeriodMs);
         advanceElapsedClock(gracePeriodMs);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job4, job4, true);
+            mQuotaController.maybeStopTrackingJobLocked(job4, job4);
         }
         expected.add(createTimingSession(start, gracePeriodMs, 1));
         assertEquals(expected,
@@ -6270,7 +6294,7 @@
         Thread.sleep(2 * gracePeriodMs);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job5, job5, true);
+            mQuotaController.maybeStopTrackingJobLocked(job5, job5);
         }
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expected,
@@ -6424,7 +6448,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobReg1, jobReg1, true);
+            mQuotaController.maybeStopTrackingJobLocked(jobReg1, jobReg1);
         }
         expectedRegular.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expectedRegular,
@@ -6440,7 +6464,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobEJ1, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobEJ1, null);
         }
         expectedEJ.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         assertEquals(expectedRegular,
@@ -6461,12 +6485,12 @@
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobEJ2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobEJ2, null);
         }
         expectedEJ.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(jobReg2, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(jobReg2, null);
         }
         expectedRegular.add(
                 createTimingSession(start + 5 * SECOND_IN_MILLIS, 10 * SECOND_IN_MILLIS, 1));
@@ -6556,7 +6580,7 @@
         }
         advanceElapsedClock(5000);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(regJob, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(regJob, null);
         }
         assertEquals(0, debit.getTallyLocked());
         assertEquals(10 * MINUTE_IN_MILLIS,
@@ -6570,7 +6594,7 @@
         }
         advanceElapsedClock(5 * MINUTE_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(eJob, null, false);
+            mQuotaController.maybeStopTrackingJobLocked(eJob, null);
         }
         assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked());
         assertEquals(5 * MINUTE_IN_MILLIS,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/StateControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/StateControllerTest.java
index 612e906..51d641b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/StateControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/StateControllerTest.java
@@ -81,8 +81,7 @@
         public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
         }
 
-        public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-                boolean forUpdate) {
+        public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) {
         }
 
         public void dumpControllerStateLocked(IndentingPrintWriter pw,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
index aa95916..f88e18b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
@@ -184,8 +184,10 @@
         when(mJobSchedulerService.isCurrentlyRunningLocked(jobLowPriorityRunning)).thenReturn(true);
         when(mJobSchedulerService.isCurrentlyRunningLocked(jobHighPriorityRunning))
                 .thenReturn(true);
-        when(mJobSchedulerService.isLongRunningLocked(jobLowPriorityRunningLong)).thenReturn(true);
-        when(mJobSchedulerService.isLongRunningLocked(jobHighPriorityRunningLong)).thenReturn(true);
+        when(mJobSchedulerService.isJobInOvertimeLocked(jobLowPriorityRunningLong))
+                .thenReturn(true);
+        when(mJobSchedulerService.isJobInOvertimeLocked(jobHighPriorityRunningLong))
+                .thenReturn(true);
 
         assertFalse(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
         assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
index 47f449c..1be7e2e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
@@ -223,7 +223,7 @@
                 /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK,
                 /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ null);
         runFullJob(mJobServiceForIdle, mJobParametersForIdle,
-                /* expectedReschedule= */ true, /* expectedStatus= */ STATUS_OK,
+                /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK,
                 /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ null);
     }
 
@@ -241,7 +241,7 @@
         assertThat(getFailedPackageNamesSecondary()).isEmpty();
 
         runFullJob(mJobServiceForIdle, mJobParametersForIdle,
-                /* expectedReschedule= */ true, /* expectedStatus= */ STATUS_OK,
+                /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK,
                 /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ PACKAGE_AAA);
 
         assertThat(getFailedPackageNamesPrimary()).containsExactly(PACKAGE_AAA);
@@ -256,7 +256,7 @@
         mDexOptResultForPackageAAA = PackageDexOptimizer.DEX_OPT_PERFORMED;
 
         runFullJob(mJobServiceForIdle, mJobParametersForIdle,
-                /* expectedReschedule= */ true, /* expectedStatus= */ STATUS_OK,
+                /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK,
                 /* totalJobFinishedWithParams= */ 2, /* expectedSkippedPackage= */ null);
 
         assertThat(getFailedPackageNamesPrimary()).isEmpty();
@@ -393,7 +393,7 @@
         mCancelThread.join(TEST_WAIT_TIMEOUT_MS);
 
         // Always reschedule for periodic job
-        verify(mJobServiceForIdle).jobFinished(mJobParametersForIdle, true);
+        verify(mJobServiceForIdle).jobFinished(mJobParametersForIdle, false);
         verifyLastControlDexOptBlockingCall(false);
     }
 
@@ -421,7 +421,7 @@
         mCancelThread.join(TEST_WAIT_TIMEOUT_MS);
 
         // Always reschedule for periodic job
-        verify(mJobServiceForIdle).jobFinished(mJobParametersForIdle, true);
+        verify(mJobServiceForIdle).jobFinished(mJobParametersForIdle, false);
         verify(mDexOptHelper, never()).controlDexOptBlocking(true);
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java
deleted file mode 100644
index 278e04a..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java
+++ /dev/null
@@ -1,258 +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.pm;
-
-import static android.os.UserHandle.USER_SYSTEM;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.junit.Assert.assertThrows;
-
-import android.util.Log;
-
-import org.junit.Test;
-
-/**
- * Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerInternalTest}
- */
-public final class UserManagerInternalTest extends UserManagerServiceOrInternalTestCase {
-
-    private static final String TAG = UserManagerInternalTest.class.getSimpleName();
-
-    // NOTE: most the tests below only apply to MUMD configurations, so we're not adding _mumd_
-    // in the test names, but _nonMumd_ instead
-
-    @Test
-    public void testAssignUserToDisplay_nonMumd_defaultDisplayIgnored() {
-        mUmi.assignUserToDisplay(USER_ID, DEFAULT_DISPLAY);
-
-        assertNoUserAssignedToDisplay();
-    }
-
-    @Test
-    public void testAssignUserToDisplay_nonMumd_otherDisplay_currentUser() {
-        mockCurrentUser(USER_ID);
-
-        assertThrows(UnsupportedOperationException.class,
-                () -> mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID));
-    }
-
-    @Test
-    public void testAssignUserToDisplay_nonMumd_otherDisplay_startProfileOfcurrentUser() {
-        mockCurrentUser(PARENT_USER_ID);
-        addDefaultProfileAndParent();
-        startDefaultProfile();
-
-        assertThrows(UnsupportedOperationException.class,
-                () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
-    }
-
-    @Test
-    public void testAssignUserToDisplay_nonMumd_otherDisplay_stoppedProfileOfcurrentUser() {
-        mockCurrentUser(PARENT_USER_ID);
-        addDefaultProfileAndParent();
-        stopDefaultProfile();
-
-        assertThrows(UnsupportedOperationException.class,
-                () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
-    }
-
-    @Test
-    public void testAssignUserToDisplay_defaultDisplayIgnored() {
-        enableUsersOnSecondaryDisplays();
-
-        mUmi.assignUserToDisplay(USER_ID, DEFAULT_DISPLAY);
-
-        assertNoUserAssignedToDisplay();
-    }
-
-    @Test
-    public void testAssignUserToDisplay_systemUser() {
-        enableUsersOnSecondaryDisplays();
-
-        assertThrows(IllegalArgumentException.class,
-                () -> mUmi.assignUserToDisplay(USER_SYSTEM, SECONDARY_DISPLAY_ID));
-    }
-
-    @Test
-    public void testAssignUserToDisplay_invalidDisplay() {
-        enableUsersOnSecondaryDisplays();
-
-        assertThrows(IllegalArgumentException.class,
-                () -> mUmi.assignUserToDisplay(USER_ID, INVALID_DISPLAY));
-    }
-
-    @Test
-    public void testAssignUserToDisplay_currentUser() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(USER_ID);
-
-        assertThrows(IllegalArgumentException.class,
-                () -> mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID));
-
-        assertNoUserAssignedToDisplay();
-    }
-
-    @Test
-    public void testAssignUserToDisplay_startedProfileOfCurrentUser() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(PARENT_USER_ID);
-        addDefaultProfileAndParent();
-        startDefaultProfile();
-
-        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
-
-        Log.v(TAG, "Exception: " + e);
-        assertNoUserAssignedToDisplay();
-    }
-
-    @Test
-    public void testAssignUserToDisplay_stoppedProfileOfCurrentUser() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(PARENT_USER_ID);
-        addDefaultProfileAndParent();
-        stopDefaultProfile();
-
-        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
-
-        Log.v(TAG, "Exception: " + e);
-        assertNoUserAssignedToDisplay();
-    }
-
-    @Test
-    public void testAssignUserToDisplay_displayAvailable() {
-        enableUsersOnSecondaryDisplays();
-
-        mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-
-        assertUserAssignedToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-    }
-
-    @Test
-    public void testAssignUserToDisplay_displayAlreadyAssigned() {
-        enableUsersOnSecondaryDisplays();
-
-        mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-
-        IllegalStateException e = assertThrows(IllegalStateException.class,
-                () -> mUmi.assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID));
-
-        Log.v(TAG, "Exception: " + e);
-        assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
-                .matches("Cannot.*" + OTHER_USER_ID + ".*" + SECONDARY_DISPLAY_ID + ".*already.*"
-                        + USER_ID + ".*");
-    }
-
-    @Test
-    public void testAssignUserToDisplay_userAlreadyAssigned() {
-        enableUsersOnSecondaryDisplays();
-
-        mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-
-        IllegalStateException e = assertThrows(IllegalStateException.class,
-                () -> mUmi.assignUserToDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID));
-
-        Log.v(TAG, "Exception: " + e);
-        assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
-                .matches("Cannot.*" + USER_ID + ".*" + OTHER_SECONDARY_DISPLAY_ID + ".*already.*"
-                        + SECONDARY_DISPLAY_ID + ".*");
-
-        assertUserAssignedToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-    }
-
-    @Test
-    public void testAssignUserToDisplay_profileOnSameDisplayAsParent() {
-        enableUsersOnSecondaryDisplays();
-        addDefaultProfileAndParent();
-
-        mUmi.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
-        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
-
-        Log.v(TAG, "Exception: " + e);
-        assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
-    }
-
-    @Test
-    public void testAssignUserToDisplay_profileOnDifferentDisplayAsParent() {
-        enableUsersOnSecondaryDisplays();
-        addDefaultProfileAndParent();
-
-        mUmi.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
-        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID));
-
-        Log.v(TAG, "Exception: " + e);
-        assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
-    }
-
-    @Test
-    public void testAssignUserToDisplay_profileDefaultDisplayParentOnSecondaryDisplay() {
-        enableUsersOnSecondaryDisplays();
-        addDefaultProfileAndParent();
-
-        mUmi.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
-        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY));
-
-        Log.v(TAG, "Exception: " + e);
-        assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
-    }
-
-    @Test
-    public void testUnassignUserFromDisplay_nonMumd_ignored() {
-        mockCurrentUser(USER_ID);
-
-        mUmi.unassignUserFromDisplay(USER_SYSTEM);
-        mUmi.unassignUserFromDisplay(USER_ID);
-        mUmi.unassignUserFromDisplay(OTHER_USER_ID);
-
-        assertNoUserAssignedToDisplay();
-    }
-
-    @Test
-    public void testUnassignUserFromDisplay() {
-        testAssignUserToDisplay_displayAvailable();
-
-        mUmi.unassignUserFromDisplay(USER_ID);
-
-        assertNoUserAssignedToDisplay();
-    }
-
-    @Override
-    protected boolean isUserVisible(int userId) {
-        return mUmi.isUserVisible(userId);
-    }
-
-    @Override
-    protected boolean isUserVisibleOnDisplay(int userId, int displayId) {
-        return mUmi.isUserVisible(userId, displayId);
-    }
-
-    @Override
-    protected int getDisplayAssignedToUser(int userId) {
-        return mUmi.getDisplayAssignedToUser(userId);
-    }
-
-    @Override
-    protected int getUserAssignedToDisplay(int displayId) {
-        return mUmi.getUserAssignedToDisplay(displayId);
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
index 90a5fa0..6c85b7a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
@@ -15,10 +15,6 @@
  */
 package com.android.server.pm;
 
-import static android.os.UserHandle.USER_NULL;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 
@@ -34,7 +30,6 @@
 import android.os.UserManager;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 
 import androidx.test.annotation.UiThreadTest;
 
@@ -46,12 +41,8 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Test;
 import org.mockito.Mock;
 
-import java.util.LinkedHashMap;
-import java.util.Map;
-
 /**
  * Base class for {@link UserManagerInternalTest} and {@link UserManagerInternalTest}.
  *
@@ -59,9 +50,13 @@
  * "symbiotic relationship - some methods of the former simply call the latter and vice versa.
  *
  * <p>Ideally, only one of them should have the logic, but since that's not the case, this class
- * provices the infra to make it easier to test both (which in turn would make it easier / safer to
+ * provides the infra to make it easier to test both (which in turn would make it easier / safer to
  * refactor their logic later).
  */
+// TODO(b/244644281): there is no UserManagerInternalTest anymore as the logic being tested there
+// moved to UserVisibilityController, so it might be simpler to merge this class into
+// UserManagerServiceTest (once the UserVisibilityController -> UserManagerService dependency is
+// fixed)
 abstract class UserManagerServiceOrInternalTestCase extends ExtendedMockitoTestCase {
 
     private static final String TAG = UserManagerServiceOrInternalTestCase.class.getSimpleName();
@@ -90,31 +85,12 @@
      */
     protected static final int PROFILE_USER_ID = 643;
 
-    /**
-     * Id of a secondary display (i.e, not {@link android.view.Display.DEFAULT_DISPLAY}).
-     */
-    protected static final int SECONDARY_DISPLAY_ID = 42;
-
-    /**
-     * Id of another secondary display (i.e, not {@link android.view.Display.DEFAULT_DISPLAY}).
-     */
-    protected static final int OTHER_SECONDARY_DISPLAY_ID = 108;
-
     private final Object mPackagesLock = new Object();
     private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation()
             .getTargetContext();
     private final SparseArray<UserData> mUsers = new SparseArray<>();
 
-    // TODO(b/244644281): manipulating mUsersOnSecondaryDisplays directly leaks implementation
-    // details into the unit test, but it's fine for now - in the long term, this logic should be
-    // refactored into a proper UserDisplayAssignment class.
-    private final SparseIntArray mUsersOnSecondaryDisplays = new SparseIntArray();
-
     private Context mSpiedContext;
-    private UserManagerService mStandardUms;
-    private UserManagerService mMumdUms;
-    private UserManagerInternal mStandardUmi;
-    private UserManagerInternal mMumdUmi;
 
     private @Mock PackageManagerService mMockPms;
     private @Mock UserDataPreparer mMockUserDataPreparer;
@@ -122,17 +98,11 @@
 
     /**
      * Reference to the {@link UserManagerService} being tested.
-     *
-     * <p>By default, such service doesn't support {@code MUMD} (Multiple Users on Multiple
-     * Displays), but that can be changed by calling {@link #enableUsersOnSecondaryDisplays()}.
      */
     protected UserManagerService mUms;
 
     /**
      * Reference to the {@link UserManagerInternal} being tested.
-     *
-     * <p>By default, such service doesn't support {@code MUMD} (Multiple Users on Multiple
-     * Displays), but that can be changed by calling {@link #enableUsersOnSecondaryDisplays()}.
      */
     protected UserManagerInternal mUmi;
 
@@ -151,32 +121,12 @@
         // Called when WatchedUserStates is constructed
         doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache());
 
-        // Need to set both UserManagerService instances here, as they need to be run in the
-        // UiThread
-
-        // mMumdUms / mMumdUmi
-        mockIsUsersOnSecondaryDisplaysEnabled(/* usersOnSecondaryDisplaysEnabled= */ true);
-        mMumdUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
-                mPackagesLock, mRealContext.getDataDir(), mUsers, mUsersOnSecondaryDisplays);
-        assertWithMessage("UserManagerService.isUsersOnSecondaryDisplaysEnabled()")
-                .that(mMumdUms.isUsersOnSecondaryDisplaysEnabled())
-                .isTrue();
-        mMumdUmi = LocalServices.getService(UserManagerInternal.class);
-        assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mMumdUmi)
+        // Must construct UserManagerService in the UiThread
+        mUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
+                mPackagesLock, mRealContext.getDataDir(), mUsers);
+        mUmi = LocalServices.getService(UserManagerInternal.class);
+        assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mUmi)
                 .isNotNull();
-        resetUserManagerInternal();
-
-        // mStandardUms / mStandardUmi
-        mockIsUsersOnSecondaryDisplaysEnabled(/* usersOnSecondaryDisplaysEnabled= */ false);
-        mStandardUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
-                mPackagesLock, mRealContext.getDataDir(), mUsers, mUsersOnSecondaryDisplays);
-        assertWithMessage("UserManagerService.isUsersOnSecondaryDisplaysEnabled()")
-                .that(mStandardUms.isUsersOnSecondaryDisplaysEnabled())
-                .isFalse();
-        mStandardUmi = LocalServices.getService(UserManagerInternal.class);
-        assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mStandardUmi)
-                .isNotNull();
-        setServiceFixtures(/*usersOnSecondaryDisplaysEnabled= */ false);
     }
 
     @After
@@ -185,373 +135,10 @@
         LocalServices.removeServiceForTest(UserManagerInternal.class);
     }
 
-    //////////////////////////////////////////////////////////////////////////////////////////////
-    // Methods whose UMS implementation calls UMI or vice-versa - they're tested in this class, //
-    // but the subclass must provide the proper implementation                                  //
-    //////////////////////////////////////////////////////////////////////////////////////////////
-
-    protected abstract boolean isUserVisible(int userId);
-    protected abstract boolean isUserVisibleOnDisplay(int userId, int displayId);
-    protected abstract int getDisplayAssignedToUser(int userId);
-    protected abstract int getUserAssignedToDisplay(int displayId);
-
-    /////////////////////////////////
-    // Tests for the above methods //
-    /////////////////////////////////
-
-    @Test
-    public void testIsUserVisible_invalidUser() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("isUserVisible(%s)", USER_NULL).that(isUserVisible(USER_NULL)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisible_currentUser() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isTrue();
-    }
-
-    @Test
-    public void testIsUserVisible_nonCurrentUser() {
-        mockCurrentUser(OTHER_USER_ID);
-
-        assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisible_startedProfileOfcurrentUser() {
-        addDefaultProfileAndParent();
-        mockCurrentUser(PARENT_USER_ID);
-        startDefaultProfile();
-        setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
-
-        assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID).that(isUserVisible(PROFILE_USER_ID))
-                .isTrue();
-    }
-
-    @Test
-    public void testIsUserVisible_stoppedProfileOfcurrentUser() {
-        addDefaultProfileAndParent();
-        mockCurrentUser(PARENT_USER_ID);
-        stopDefaultProfile();
-
-        assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID).that(isUserVisible(PROFILE_USER_ID))
-                .isFalse();
-    }
-
-    @Test
-    public void testIsUserVisible_bgUserOnSecondaryDisplay() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(OTHER_USER_ID);
-        assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-
-        assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isTrue();
-    }
-
-    // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
-    // isUserVisible() for bg users relies only on the user / display assignments
-
-    @Test
-    public void testIsUserVisibleOnDisplay_invalidUser() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_NULL, DEFAULT_DISPLAY)
-                .that(isUserVisibleOnDisplay(USER_NULL, DEFAULT_DISPLAY)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_currentUserInvalidDisplay() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, INVALID_DISPLAY)
-                .that(isUserVisibleOnDisplay(USER_ID, INVALID_DISPLAY)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_currentUserDefaultDisplay() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, DEFAULT_DISPLAY)
-                .that(isUserVisibleOnDisplay(USER_ID, DEFAULT_DISPLAY)).isTrue();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_currentUserSecondaryDisplay() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
-                .that(isUserVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_mumd_currentUserUnassignedSecondaryDisplay() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
-                .that(isUserVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_mumd_currentUserSecondaryDisplayAssignedToAnotherUser() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(USER_ID);
-        assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
-                .that(isUserVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_mumd_startedProfileOfCurrentUserSecondaryDisplayAssignedToAnotherUser() {
-        enableUsersOnSecondaryDisplays();
-        addDefaultProfileAndParent();
-        startDefaultProfile();
-        mockCurrentUser(PARENT_USER_ID);
-        assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
-                .that(isUserVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_mumd_stoppedProfileOfCurrentUserSecondaryDisplayAssignedToAnotherUser() {
-        enableUsersOnSecondaryDisplays();
-        addDefaultProfileAndParent();
-        stopDefaultProfile();
-        mockCurrentUser(PARENT_USER_ID);
-        assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
-                .that(isUserVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_nonCurrentUserDefaultDisplay() {
-        mockCurrentUser(OTHER_USER_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, DEFAULT_DISPLAY)
-                .that(isUserVisibleOnDisplay(USER_ID, DEFAULT_DISPLAY)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserInvalidDisplay() {
-        addDefaultProfileAndParent();
-        mockCurrentUser(PARENT_USER_ID);
-        startDefaultProfile();
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY)
-                .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserInvalidDisplay() {
-        addDefaultProfileAndParent();
-        mockCurrentUser(PARENT_USER_ID);
-        stopDefaultProfile();
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY)
-                .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserDefaultDisplay() {
-        addDefaultProfileAndParent();
-        mockCurrentUser(PARENT_USER_ID);
-        startDefaultProfile();
-        setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY)
-                .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserDefaultDisplay() {
-        addDefaultProfileAndParent();
-        mockCurrentUser(PARENT_USER_ID);
-        stopDefaultProfile();
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY)
-                .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserSecondaryDisplay() {
-        addDefaultProfileAndParent();
-        mockCurrentUser(PARENT_USER_ID);
-        startDefaultProfile();
-        setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
-                .that(isUserVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserSecondaryDisplay() {
-        addDefaultProfileAndParent();
-        mockCurrentUser(PARENT_USER_ID);
-        stopDefaultProfile();
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
-                .that(isUserVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isFalse();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_mumd_bgUserOnSecondaryDisplay() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(OTHER_USER_ID);
-        assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
-                .that(isUserVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
-    }
-
-    @Test
-    public void testIsUserVisibleOnDisplay_mumd_bgUserOnAnotherSecondaryDisplay() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(OTHER_USER_ID);
-        assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-
-        assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
-                .that(isUserVisibleOnDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID)).isFalse();
-    }
-
-    // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
-    // isUserVisibleOnDisplay() for bg users relies only on the user / display assignments
-
-    @Test
-    public void testGetDisplayAssignedToUser_invalidUser() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("getDisplayAssignedToUser(%s)", USER_NULL)
-                .that(getDisplayAssignedToUser(USER_NULL)).isEqualTo(INVALID_DISPLAY);
-    }
-
-    @Test
-    public void testGetDisplayAssignedToUser_currentUser() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID)
-                .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(DEFAULT_DISPLAY);
-    }
-
-    @Test
-    public void testGetDisplayAssignedToUser_nonCurrentUser() {
-        mockCurrentUser(OTHER_USER_ID);
-
-        assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID)
-                .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(INVALID_DISPLAY);
-    }
-
-    @Test
-    public void testGetDisplayAssignedToUser_startedProfileOfcurrentUser() {
-        addDefaultProfileAndParent();
-        mockCurrentUser(PARENT_USER_ID);
-        startDefaultProfile();
-        setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
-
-        assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID)
-                .that(getDisplayAssignedToUser(PROFILE_USER_ID)).isEqualTo(DEFAULT_DISPLAY);
-    }
-
-    @Test
-    public void testGetDisplayAssignedToUser_stoppedProfileOfcurrentUser() {
-        addDefaultProfileAndParent();
-        mockCurrentUser(PARENT_USER_ID);
-        stopDefaultProfile();
-
-        assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID)
-                .that(getDisplayAssignedToUser(PROFILE_USER_ID)).isEqualTo(INVALID_DISPLAY);
-    }
-
-    @Test
-    public void testGetDisplayAssignedToUser_bgUserOnSecondaryDisplay() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(OTHER_USER_ID);
-        assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-
-        assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID)
-                .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(SECONDARY_DISPLAY_ID);
-    }
-
-    // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
-    // getDisplayAssignedToUser() for bg users relies only on the user / display assignments
-
-    @Test
-    public void testGetUserAssignedToDisplay_invalidDisplay() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("getUserAssignedToDisplay(%s)", INVALID_DISPLAY)
-                .that(getUserAssignedToDisplay(INVALID_DISPLAY)).isEqualTo(USER_ID);
-    }
-
-    @Test
-    public void testGetUserAssignedToDisplay_defaultDisplay() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("getUserAssignedToDisplay(%s)", DEFAULT_DISPLAY)
-                .that(getUserAssignedToDisplay(DEFAULT_DISPLAY)).isEqualTo(USER_ID);
-    }
-
-    @Test
-    public void testGetUserAssignedToDisplay_secondaryDisplay() {
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
-                .that(getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
-    }
-
-    @Test
-    public void testGetUserAssignedToDisplay_mumd_bgUserOnSecondaryDisplay() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(OTHER_USER_ID);
-        assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-
-        assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
-                .that(getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
-    }
-
-    @Test
-    public void testGetUserAssignedToDisplay_mumd_noUserOnSecondaryDisplay() {
-        enableUsersOnSecondaryDisplays();
-        mockCurrentUser(USER_ID);
-
-        assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
-                .that(getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
-    }
-
-    // TODO(b/244644281): scenario below shouldn't happen on "real life", as the profile cannot be
-    // started on secondary display if its parent isn't, so we might need to remove (or refactor
-    // this test) if/when the underlying logic changes
-    @Test
-    public void testGetUserAssignedToDisplay_mumd_profileOnSecondaryDisplay() {
-        enableUsersOnSecondaryDisplays();
-        addDefaultProfileAndParent();
-        mockCurrentUser(USER_ID);
-        assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
-
-        assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
-                .that(getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
-    }
-
-    // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
-    // getUserAssignedToDisplay() for bg users relies only on the user / display assignments
-
-
     ///////////////////////////////////////////
     // Helper methods exposed to sub-classes //
     ///////////////////////////////////////////
 
-    /**
-     * Change test fixtures to use a version that supports {@code MUMD} (Multiple Users on Multiple
-     * Displays).
-     */
-    protected final void enableUsersOnSecondaryDisplays() {
-        setServiceFixtures(/* usersOnSecondaryDisplaysEnabled= */ true);
-    }
-
     protected final void mockCurrentUser(@UserIdInt int userId) {
         mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
 
@@ -597,65 +184,19 @@
         setUserState(userId, UserState.STATE_STOPPING);
     }
 
-    // NOTE: should only called by tests that indirectly needs to check user assignments (like
-    // isUserVisible), not by tests for the user assignment methods per se.
-    protected final void assignUserToDisplay(@UserIdInt int userId, int displayId) {
-        mUsersOnSecondaryDisplays.put(userId, displayId);
-    }
-
-    protected final void assertNoUserAssignedToDisplay() {
-        assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap())
-                .isEmpty();
-    }
-
-    protected final void assertUserAssignedToDisplay(@UserIdInt int userId, int displayId) {
-        assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap())
-                .containsExactly(userId, displayId);
+    protected final void setUserState(@UserIdInt int userId, int userState) {
+        mUmi.setUserState(userId, userState);
     }
 
     ///////////////////
     // Private infra //
     ///////////////////
 
-    private void setServiceFixtures(boolean usersOnSecondaryDisplaysEnabled) {
-        Log.d(TAG, "Setting fixtures for usersOnSecondaryDisplaysEnabled="
-                + usersOnSecondaryDisplaysEnabled);
-        if (usersOnSecondaryDisplaysEnabled) {
-            mUms = mMumdUms;
-            mUmi = mMumdUmi;
-        } else {
-            mUms = mStandardUms;
-            mUmi = mStandardUmi;
-        }
-    }
-
-    private void mockIsUsersOnSecondaryDisplaysEnabled(boolean enabled) {
-        Log.d(TAG, "Mocking UserManager.isUsersOnSecondaryDisplaysEnabled() to return " + enabled);
-        doReturn(enabled).when(() -> UserManager.isUsersOnSecondaryDisplaysEnabled());
-    }
-
     private void addUserData(TestUserData userData) {
         Log.d(TAG, "Adding " + userData);
         mUsers.put(userData.info.id, userData);
     }
 
-    private void setUserState(@UserIdInt int userId, int userState) {
-        mUmi.setUserState(userId, userState);
-    }
-
-    private void removeUserState(@UserIdInt int userId) {
-        mUmi.removeUserState(userId);
-    }
-
-    private Map<Integer, Integer> usersOnSecondaryDisplaysAsMap() {
-        int size = mUsersOnSecondaryDisplays.size();
-        Map<Integer, Integer> map = new LinkedHashMap<>(size);
-        for (int i = 0; i < size; i++) {
-            map.put(mUsersOnSecondaryDisplays.keyAt(i), mUsersOnSecondaryDisplays.valueAt(i));
-        }
-        return map;
-    }
-
     private static final class TestUserData extends UserData {
 
         @SuppressWarnings("deprecation")
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 8b5921c..b5ffe5f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -123,24 +123,4 @@
         assertWithMessage("isUserRunning(%s)", PROFILE_USER_ID)
                 .that(mUms.isUserRunning(PROFILE_USER_ID)).isFalse();
     }
-
-    @Override
-    protected boolean isUserVisible(int userId) {
-        return mUms.isUserVisibleUnchecked(userId);
-    }
-
-    @Override
-    protected boolean isUserVisibleOnDisplay(int userId, int displayId) {
-        return mUms.isUserVisibleOnDisplay(userId, displayId);
-    }
-
-    @Override
-    protected int getDisplayAssignedToUser(int userId) {
-        return mUms.getDisplayAssignedToUser(userId);
-    }
-
-    @Override
-    protected int getUserAssignedToDisplay(int displayId) {
-        return mUms.getUserAssignedToDisplay(displayId);
-    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java
new file mode 100644
index 0000000..21f541f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.pm;
+
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+
+import android.util.Log;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link UserVisibilityMediator} tests for devices that support concurrent Multiple
+ * Users on Multiple Displays (A.K.A {@code MUMD}).
+ *
+ * <p>Run as
+ * {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserVisibilityMediatorMUMDTest}
+ */
+public final class UserVisibilityMediatorMUMDTest extends UserVisibilityMediatorTestCase {
+
+    private static final String TAG = UserVisibilityMediatorMUMDTest.class.getSimpleName();
+
+    public UserVisibilityMediatorMUMDTest() {
+        super(/* usersOnSecondaryDisplaysEnabled= */ true);
+    }
+
+    @Test
+    public void testAssignUserToDisplay_systemUser() {
+        assertThrows(IllegalArgumentException.class,
+                () -> mMediator.assignUserToDisplay(USER_SYSTEM, SECONDARY_DISPLAY_ID));
+    }
+
+    @Test
+    public void testAssignUserToDisplay_invalidDisplay() {
+        assertThrows(IllegalArgumentException.class,
+                () -> mMediator.assignUserToDisplay(USER_ID, INVALID_DISPLAY));
+    }
+
+    @Test
+    public void testAssignUserToDisplay_currentUser() {
+        mockCurrentUser(USER_ID);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mMediator.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID));
+
+        assertNoUserAssignedToDisplay();
+    }
+
+    @Test
+    public void testAssignUserToDisplay_startedProfileOfCurrentUser() {
+        mockCurrentUser(PARENT_USER_ID);
+        addDefaultProfileAndParent();
+        startDefaultProfile();
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+
+        Log.v(TAG, "Exception: " + e);
+        assertNoUserAssignedToDisplay();
+    }
+
+    @Test
+    public void testAssignUserToDisplay_stoppedProfileOfCurrentUser() {
+        mockCurrentUser(PARENT_USER_ID);
+        addDefaultProfileAndParent();
+        stopDefaultProfile();
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+
+        Log.v(TAG, "Exception: " + e);
+        assertNoUserAssignedToDisplay();
+    }
+
+    @Test
+    public void testAssignUserToDisplay_displayAvailable() {
+        mMediator.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+        assertUserAssignedToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+    }
+
+    @Test
+    public void testAssignUserToDisplay_displayAlreadyAssigned() {
+        mMediator.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+        IllegalStateException e = assertThrows(IllegalStateException.class,
+                () -> mMediator.assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID));
+
+        Log.v(TAG, "Exception: " + e);
+        assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
+                .matches("Cannot.*" + OTHER_USER_ID + ".*" + SECONDARY_DISPLAY_ID + ".*already.*"
+                        + USER_ID + ".*");
+    }
+
+    @Test
+    public void testAssignUserToDisplay_userAlreadyAssigned() {
+        mMediator.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+        IllegalStateException e = assertThrows(IllegalStateException.class,
+                () -> mMediator.assignUserToDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID));
+
+        Log.v(TAG, "Exception: " + e);
+        assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
+                .matches("Cannot.*" + USER_ID + ".*" + OTHER_SECONDARY_DISPLAY_ID + ".*already.*"
+                        + SECONDARY_DISPLAY_ID + ".*");
+
+        assertUserAssignedToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+    }
+
+    @Test
+    public void testAssignUserToDisplay_profileOnSameDisplayAsParent() {
+        addDefaultProfileAndParent();
+
+        mMediator.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+
+        Log.v(TAG, "Exception: " + e);
+        assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+    }
+
+    @Test
+    public void testAssignUserToDisplay_profileOnDifferentDisplayAsParent() {
+        addDefaultProfileAndParent();
+
+        mMediator.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID));
+
+        Log.v(TAG, "Exception: " + e);
+        assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+    }
+
+    @Test
+    public void testAssignUserToDisplay_profileDefaultDisplayParentOnSecondaryDisplay() {
+        addDefaultProfileAndParent();
+
+        mMediator.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY));
+
+        Log.v(TAG, "Exception: " + e);
+        assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+    }
+
+    @Test
+    public void testUnassignUserFromDisplay() {
+        testAssignUserToDisplay_displayAvailable();
+
+        mMediator.unassignUserFromDisplay(USER_ID);
+
+        assertNoUserAssignedToDisplay();
+    }
+
+    @Test
+    public void testIsUserVisible_bgUserOnSecondaryDisplay() {
+        mockCurrentUser(OTHER_USER_ID);
+        assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+        assertWithMessage("isUserVisible(%s)", USER_ID)
+                .that(mMediator.isUserVisible(USER_ID)).isTrue();
+    }
+
+    // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
+    // isUserVisible() for bg users relies only on the user / display assignments
+
+    @Test
+    public void testIsUserVisibleOnDisplay_currentUserUnassignedSecondaryDisplay() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
+                .that(mMediator.isUserVisible(USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
+    }
+
+    @Test
+    public void testIsUserVisibleOnDisplay_currentUserSecondaryDisplayAssignedToAnotherUser() {
+        mockCurrentUser(USER_ID);
+        assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
+                .that(mMediator.isUserVisible(USER_ID, SECONDARY_DISPLAY_ID)).isFalse();
+    }
+
+    @Test
+    public void testIsUserVisibleOnDisplay_startedProfileOfCurrentUserSecondaryDisplayAssignedToAnotherUser() {
+        addDefaultProfileAndParent();
+        startDefaultProfile();
+        mockCurrentUser(PARENT_USER_ID);
+        assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isFalse();
+    }
+
+    @Test
+    public void testIsUserVisibleOnDisplay_stoppedProfileOfCurrentUserSecondaryDisplayAssignedToAnotherUser() {
+        addDefaultProfileAndParent();
+        stopDefaultProfile();
+        mockCurrentUser(PARENT_USER_ID);
+        assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isFalse();
+    }
+
+    @Test
+    public void testIsUserVisibleOnDisplay_startedProfileOfCurrentUserOnUnassignedSecondaryDisplay() {
+        addDefaultProfileAndParent();
+        startDefaultProfile();
+        mockCurrentUser(PARENT_USER_ID);
+
+        // TODO(b/244644281): change it to isFalse() once isUserVisible() is fixed (see note there)
+        assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
+    }
+
+    @Test
+    public void testIsUserVisibleOnDisplay_bgUserOnSecondaryDisplay() {
+        mockCurrentUser(OTHER_USER_ID);
+        assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
+                .that(mMediator.isUserVisible(USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
+    }
+
+    @Test
+    public void testIsUserVisibleOnDisplay_bgUserOnAnotherSecondaryDisplay() {
+        mockCurrentUser(OTHER_USER_ID);
+        assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
+                .that(mMediator.isUserVisible(USER_ID, OTHER_SECONDARY_DISPLAY_ID)).isFalse();
+    }
+
+    // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
+    // the tests for isUserVisible(userId, display) for non-current users relies on the explicit
+    // user / display assignments
+    // TODO(b/244644281): add such tests if the logic change
+
+    @Test
+    public void testGetDisplayAssignedToUser_bgUserOnSecondaryDisplay() {
+        mockCurrentUser(OTHER_USER_ID);
+        assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+        assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID)
+                .that(mMediator.getDisplayAssignedToUser(USER_ID))
+                .isEqualTo(SECONDARY_DISPLAY_ID);
+    }
+
+    // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
+    // getDisplayAssignedToUser() for bg users relies only on the user / display assignments
+
+    @Test
+    public void testGetUserAssignedToDisplay_bgUserOnSecondaryDisplay() {
+        mockCurrentUser(OTHER_USER_ID);
+        assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+        assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
+                .that(mMediator.getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
+    }
+
+    @Test
+    public void testGetUserAssignedToDisplay_noUserOnSecondaryDisplay() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
+                .that(mMediator.getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
+    }
+
+    // TODO(b/244644281): scenario below shouldn't happen on "real life", as the profile cannot be
+    // started on secondary display if its parent isn't, so we might need to remove (or refactor
+    // this test) if/when the underlying logic changes
+    @Test
+    public void testGetUserAssignedToDisplay_profileOnSecondaryDisplay() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(USER_ID);
+        assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
+
+        assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
+                .that(mMediator.getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
+    }
+
+    // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
+    // getUserAssignedToDisplay() for bg users relies only on the user / display assignments
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java
new file mode 100644
index 0000000..7ae8117
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.pm;
+
+import static android.os.UserHandle.USER_SYSTEM;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link UserVisibilityMediator} tests for devices that DO NOT support concurrent
+ * multiple users on multiple displays (A.K.A {@code SUSD} - Single User on Single Device).
+ *
+ * <p>Run as
+ * {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserVisibilityMediatorSUSDTest}
+ */
+public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediatorTestCase {
+
+    public UserVisibilityMediatorSUSDTest() {
+        super(/* usersOnSecondaryDisplaysEnabled= */ false);
+    }
+
+    @Test
+    public void testAssignUserToDisplay_otherDisplay_currentUser() {
+        mockCurrentUser(USER_ID);
+
+        assertThrows(UnsupportedOperationException.class,
+                () -> mMediator.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID));
+    }
+
+    @Test
+    public void testAssignUserToDisplay_otherDisplay_startProfileOfcurrentUser() {
+        mockCurrentUser(PARENT_USER_ID);
+        addDefaultProfileAndParent();
+        startDefaultProfile();
+
+        assertThrows(UnsupportedOperationException.class,
+                () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+    }
+
+    @Test
+    public void testAssignUserToDisplay_otherDisplay_stoppedProfileOfcurrentUser() {
+        mockCurrentUser(PARENT_USER_ID);
+        addDefaultProfileAndParent();
+        stopDefaultProfile();
+
+        assertThrows(UnsupportedOperationException.class,
+                () -> mMediator.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+    }
+
+    @Test
+    public void testUnassignUserFromDisplay_ignored() {
+        mockCurrentUser(USER_ID);
+
+        mMediator.unassignUserFromDisplay(USER_SYSTEM);
+        mMediator.unassignUserFromDisplay(USER_ID);
+        mMediator.unassignUserFromDisplay(OTHER_USER_ID);
+
+        assertNoUserAssignedToDisplay();
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
new file mode 100644
index 0000000..22e6e0d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
@@ -0,0 +1,323 @@
+/*
+ * 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.pm;
+
+import static android.os.UserHandle.USER_NULL;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.UserIdInt;
+import android.util.SparseIntArray;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Base class for {@link UserVisibilityMediator} tests.
+ *
+ * <p>It contains common logics and tests for behaviors that should be invariant regardless of the
+ * device mode (for example, whether the device supports concurrent multiple users on multiple
+ * displays or not).
+ */
+abstract class UserVisibilityMediatorTestCase extends UserManagerServiceOrInternalTestCase {
+
+    /**
+     * Id of a secondary display (i.e, not {@link android.view.Display.DEFAULT_DISPLAY}).
+     */
+    protected static final int SECONDARY_DISPLAY_ID = 42;
+
+    /**
+     * Id of another secondary display (i.e, not {@link android.view.Display.DEFAULT_DISPLAY}).
+     */
+    protected static final int OTHER_SECONDARY_DISPLAY_ID = 108;
+
+    private final boolean mUsersOnSecondaryDisplaysEnabled;
+
+    // TODO(b/244644281): manipulating mUsersOnSecondaryDisplays directly leaks implementation
+    // details into the unit test, but it's fine for now as the tests were copied "as is" - it
+    // would be better to use a geter() instead
+    protected final SparseIntArray mUsersOnSecondaryDisplays = new SparseIntArray();
+
+    protected UserVisibilityMediator mMediator;
+
+    protected UserVisibilityMediatorTestCase(boolean usersOnSecondaryDisplaysEnabled) {
+        mUsersOnSecondaryDisplaysEnabled = usersOnSecondaryDisplaysEnabled;
+    }
+
+    @Before
+    public final void setMediator() {
+        mMediator = new UserVisibilityMediator(mUms, mUsersOnSecondaryDisplaysEnabled,
+                mUsersOnSecondaryDisplays);
+    }
+
+    @Test
+    public final void testAssignUserToDisplay_defaultDisplayIgnored() {
+        mMediator.assignUserToDisplay(USER_ID, DEFAULT_DISPLAY);
+
+        assertNoUserAssignedToDisplay();
+    }
+
+    @Test
+    public final void testIsUserVisible_invalidUser() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("isUserVisible(%s)", USER_NULL)
+                .that(mMediator.isUserVisible(USER_NULL)).isFalse();
+    }
+
+    @Test
+    public final void testIsUserVisible_currentUser() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("isUserVisible(%s)", USER_ID)
+                .that(mMediator.isUserVisible(USER_ID)).isTrue();
+    }
+
+    @Test
+    public final void testIsUserVisible_nonCurrentUser() {
+        mockCurrentUser(OTHER_USER_ID);
+
+        assertWithMessage("isUserVisible(%s)", USER_ID)
+                .that(mMediator.isUserVisible(USER_ID)).isFalse();
+    }
+
+    @Test
+    public final void testIsUserVisible_startedProfileOfcurrentUser() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(PARENT_USER_ID);
+        startDefaultProfile();
+        setUserState(PROFILE_USER_ID, STATE_RUNNING_UNLOCKED);
+
+        assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID)).isTrue();
+    }
+
+    @Test
+    public final void testIsUserVisible_stoppedProfileOfcurrentUser() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(PARENT_USER_ID);
+        stopDefaultProfile();
+
+        assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID)).isFalse();
+    }
+
+    @Test
+    public final void testIsUserVisibleOnDisplay_invalidUser() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", USER_NULL, DEFAULT_DISPLAY)
+                .that(mMediator.isUserVisible(USER_NULL, DEFAULT_DISPLAY)).isFalse();
+    }
+
+    @Test
+    public final void testIsUserVisibleOnDisplay_currentUserInvalidDisplay() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", USER_ID, INVALID_DISPLAY)
+                .that(mMediator.isUserVisible(USER_ID, INVALID_DISPLAY)).isFalse();
+    }
+
+    @Test
+    public final void testIsUserVisibleOnDisplay_currentUserDefaultDisplay() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", USER_ID, DEFAULT_DISPLAY)
+                .that(mMediator.isUserVisible(USER_ID, DEFAULT_DISPLAY)).isTrue();
+    }
+
+    @Test
+    public final void testIsUserVisibleOnDisplay_currentUserSecondaryDisplay() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
+                .that(mMediator.isUserVisible(USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
+    }
+
+    @Test
+    public final void testIsUserVisibleOnDisplay_nonCurrentUserDefaultDisplay() {
+        mockCurrentUser(OTHER_USER_ID);
+
+        assertWithMessage("isUserVisible(%s, %s)", USER_ID, DEFAULT_DISPLAY)
+                .that(mMediator.isUserVisible(USER_ID, DEFAULT_DISPLAY)).isFalse();
+    }
+
+    @Test
+    public final void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserInvalidDisplay() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(PARENT_USER_ID);
+        startDefaultProfile();
+
+        assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue();
+    }
+
+    @Test
+    public final void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserInvalidDisplay() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(PARENT_USER_ID);
+        stopDefaultProfile();
+
+        assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID, DEFAULT_DISPLAY)).isFalse();
+    }
+
+    @Test
+    public final void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserDefaultDisplay() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(PARENT_USER_ID);
+        startDefaultProfile();
+        setUserState(PROFILE_USER_ID, STATE_RUNNING_UNLOCKED);
+
+        assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue();
+    }
+
+    @Test
+    public final void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserDefaultDisplay() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(PARENT_USER_ID);
+        stopDefaultProfile();
+
+        assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID, DEFAULT_DISPLAY)).isFalse();
+    }
+
+    @Test
+    public final void testIsUserVisibleOnDisplay_startedProfileOfCurrentUserSecondaryDisplay() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(PARENT_USER_ID);
+        startDefaultProfile();
+        setUserState(PROFILE_USER_ID, STATE_RUNNING_UNLOCKED);
+
+        assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
+    }
+
+    @Test
+    public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserSecondaryDisplay() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(PARENT_USER_ID);
+        stopDefaultProfile();
+
+        assertWithMessage("isUserVisible(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
+                .that(mMediator.isUserVisible(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isFalse();
+    }
+
+    @Test
+    public void testGetDisplayAssignedToUser_invalidUser() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("getDisplayAssignedToUser(%s)", USER_NULL)
+                .that(mMediator.getDisplayAssignedToUser(USER_NULL)).isEqualTo(INVALID_DISPLAY);
+    }
+
+    @Test
+    public void testGetDisplayAssignedToUser_currentUser() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID)
+                .that(mMediator.getDisplayAssignedToUser(USER_ID)).isEqualTo(DEFAULT_DISPLAY);
+    }
+
+    @Test
+    public final void testGetDisplayAssignedToUser_nonCurrentUser() {
+        mockCurrentUser(OTHER_USER_ID);
+
+        assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID)
+                .that(mMediator.getDisplayAssignedToUser(USER_ID)).isEqualTo(INVALID_DISPLAY);
+    }
+
+    @Test
+    public final void testGetDisplayAssignedToUser_startedProfileOfcurrentUser() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(PARENT_USER_ID);
+        startDefaultProfile();
+        setUserState(PROFILE_USER_ID, STATE_RUNNING_UNLOCKED);
+
+        assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID)
+                .that(mMediator.getDisplayAssignedToUser(PROFILE_USER_ID))
+                .isEqualTo(DEFAULT_DISPLAY);
+    }
+
+    @Test
+    public final void testGetDisplayAssignedToUser_stoppedProfileOfcurrentUser() {
+        addDefaultProfileAndParent();
+        mockCurrentUser(PARENT_USER_ID);
+        stopDefaultProfile();
+
+        assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID)
+                .that(mMediator.getDisplayAssignedToUser(PROFILE_USER_ID))
+                .isEqualTo(INVALID_DISPLAY);
+    }
+
+    @Test
+    public void testGetUserAssignedToDisplay_invalidDisplay() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("getUserAssignedToDisplay(%s)", INVALID_DISPLAY)
+                .that(mMediator.getUserAssignedToDisplay(INVALID_DISPLAY)).isEqualTo(USER_ID);
+    }
+
+    @Test
+    public final void testGetUserAssignedToDisplay_defaultDisplay() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("getUserAssignedToDisplay(%s)", DEFAULT_DISPLAY)
+                .that(mMediator.getUserAssignedToDisplay(DEFAULT_DISPLAY)).isEqualTo(USER_ID);
+    }
+
+    @Test
+    public final void testGetUserAssignedToDisplay_secondaryDisplay() {
+        mockCurrentUser(USER_ID);
+
+        assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
+                .that(mMediator.getUserAssignedToDisplay(SECONDARY_DISPLAY_ID))
+                .isEqualTo(USER_ID);
+    }
+
+    // NOTE: should only called by tests that indirectly needs to check user assignments (like
+    // isUserVisible), not by tests for the user assignment methods per se.
+    protected final void assignUserToDisplay(@UserIdInt int userId, int displayId) {
+        mUsersOnSecondaryDisplays.put(userId, displayId);
+    }
+
+    protected final void assertNoUserAssignedToDisplay() {
+        assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap())
+                .isEmpty();
+    }
+
+    protected final void assertUserAssignedToDisplay(@UserIdInt int userId, int displayId) {
+        assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap())
+                .containsExactly(userId, displayId);
+    }
+
+    private Map<Integer, Integer> usersOnSecondaryDisplaysAsMap() {
+        int size = mUsersOnSecondaryDisplays.size();
+        Map<Integer, Integer> map = new LinkedHashMap<>(size);
+        for (int i = 0; i < size; i++) {
+            map.put(mUsersOnSecondaryDisplays.keyAt(i), mUsersOnSecondaryDisplays.valueAt(i));
+        }
+        return map;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
index 2fac31e..d477cb6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
@@ -73,7 +73,12 @@
         }
 
         @Override
-        long getHardSatiatedConsumptionLimit() {
+        long getMinSatiatedConsumptionLimit() {
+            return 0;
+        }
+
+        @Override
+        long getMaxSatiatedConsumptionLimit() {
             return 0;
         }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java
index fb3e8f2..84a61c7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java
@@ -132,8 +132,10 @@
     public void testDefaults() {
         assertEquals(EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
                 mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES,
-                mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         final String pkgRestricted = "com.pkg.restricted";
         when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
         assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
@@ -150,13 +152,15 @@
     @Test
     public void testConstantsUpdating_ValidValues() {
         setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
-        setDeviceConfigCakes(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT, arcToCake(25));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT, arcToCake(3));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT, arcToCake(25));
         setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(10));
         setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(9));
         setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(7));
 
         assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(arcToCake(25), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(3), mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(arcToCake(25), mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         final String pkgRestricted = "com.pkg.restricted";
         when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
         assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
@@ -171,13 +175,15 @@
     public void testConstantsUpdating_InvalidValues() {
         // Test negatives.
         setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(-5));
-        setDeviceConfigCakes(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT, arcToCake(-5));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT, arcToCake(-5));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT, arcToCake(-5));
         setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(-1));
         setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(-2));
         setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(-3));
 
         assertEquals(arcToCake(1), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(arcToCake(1), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(1), mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(arcToCake(1), mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         final String pkgRestricted = "com.pkg.restricted";
         when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
         assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
@@ -188,14 +194,16 @@
         assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
 
         // Test min+max reversed.
-        setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
-        setDeviceConfigCakes(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT, arcToCake(3));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT, arcToCake(5));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(4));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT, arcToCake(3));
         setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(10));
         setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(11));
         setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(13));
 
         assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(arcToCake(5), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(arcToCake(5), mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
         assertEquals(arcToCake(13), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
         assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java
index 6da4ab7..cad608f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java
@@ -155,9 +155,12 @@
         assertEquals(EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES
                 + EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
                 mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES
-                + EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES,
-                mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES
+                + EconomyManager.DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES
+                + EconomyManager.DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         final String pkgRestricted = "com.pkg.restricted";
         when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
         assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
@@ -178,8 +181,10 @@
     public void testConstantsUpdated() {
         setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(4));
         setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(6));
-        setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(24));
-        setDeviceConfigCakes(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT, arcToCake(26));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(2));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT, arcToCake(3));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(24));
+        setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT, arcToCake(26));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(9));
         setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(11));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(8));
@@ -188,7 +193,8 @@
         setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(2));
 
         assertEquals(arcToCake(10), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(arcToCake(50), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(arcToCake(50), mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         final String pkgRestricted = "com.pkg.restricted";
         when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
         assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
@@ -206,8 +212,10 @@
         setDeviceConfigBoolean(EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER, false);
         assertEquals(EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
                 mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES,
-                mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         final String pkgRestricted = "com.pkg.restricted";
         when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
         assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
@@ -229,8 +237,10 @@
         setDeviceConfigBoolean(EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER, true);
         assertEquals(EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
                 mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES,
-                mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
         assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
         assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
index b7bbcd75..ebf760c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
@@ -132,8 +132,10 @@
     public void testDefaults() {
         assertEquals(EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
                 mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES,
-                mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES,
+                mEconomicPolicy.getMaxSatiatedConsumptionLimit());
 
         final String pkgRestricted = "com.pkg.restricted";
         when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
@@ -171,7 +173,8 @@
     @Test
     public void testConstantsUpdating_ValidValues() {
         setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
-        setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(25));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(2));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(25));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(6));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(4));
@@ -179,7 +182,8 @@
                 arcToCake(1));
 
         assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(arcToCake(25), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(2), mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(arcToCake(25), mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         final String pkgRestricted = "com.pkg.restricted";
         when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
         assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
@@ -198,7 +202,8 @@
     public void testConstantsUpdating_InvalidValues() {
         // Test negatives.
         setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(-5));
-        setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(-5));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(-5));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(-5));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(-1));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(-2));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(-3));
@@ -206,7 +211,8 @@
                 arcToCake(-4));
 
         assertEquals(arcToCake(1), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(arcToCake(1), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(1), mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(arcToCake(1), mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         final String pkgRestricted = "com.pkg.restricted";
         when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
         assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
@@ -221,14 +227,16 @@
                 mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
 
         // Test min+max reversed.
-        setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
-        setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(3));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(5));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(4));
+        setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(3));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(11));
         setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(13));
 
         assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
-        assertEquals(arcToCake(5), mEconomicPolicy.getHardSatiatedConsumptionLimit());
+        assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedConsumptionLimit());
+        assertEquals(arcToCake(5), mEconomicPolicy.getMaxSatiatedConsumptionLimit());
         assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
         assertEquals(arcToCake(13), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
         assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
index 00d7541..a3a49d70 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
@@ -35,6 +35,7 @@
 
 import android.app.AlarmManager;
 import android.content.Context;
+import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.util.ArraySet;
@@ -222,7 +223,8 @@
 
         alarmQueue.addAlarm("com.android.test.1", nowElapsed + HOUR_IN_MILLIS);
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(nowElapsed + HOUR_IN_MILLIS), anyLong(), eq(ALARM_TAG), any(), any());
+                anyInt(), eq(nowElapsed + HOUR_IN_MILLIS), anyLong(), eq(ALARM_TAG), any(), any(
+                        Handler.class));
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
index 608b64e..0d14c9f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
@@ -589,14 +589,14 @@
         // No sessions saved yet.
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, never()).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions out of window.
         final long now = mInjector.getElapsedRealtime();
         logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 10 * HOUR_IN_MILLIS, 20);
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, never()).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions in window but still in quota.
         final long start = now - (6 * HOUR_IN_MILLIS);
@@ -604,25 +604,27 @@
         logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, start, 5);
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, never()).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Add some more sessions, but still in quota.
         logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 3 * HOUR_IN_MILLIS, 1);
         logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS, 3);
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, never()).setWindow(
-                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test when out of quota.
         logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS, 1);
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
 
         // Alarm already scheduled, so make sure it's not scheduled again.
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, times(1)).setWindow(
-                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any(Handler.class));
     }
 
     /** Tests that the start alarm is properly rescheduled if the app's category is changed. */
@@ -656,7 +658,8 @@
         mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, never())
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
         inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class));
 
         // And down from there.
@@ -665,41 +668,42 @@
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .setWindow(anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
-                        eq(TAG_QUOTA_CHECK), any(), any());
+                        eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         final long expectedFrequentAlarmTime = outOfQuotaTime + (8 * HOUR_IN_MILLIS);
         mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .setWindow(anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
-                        eq(TAG_QUOTA_CHECK), any(), any());
+                        eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         final long expectedRareAlarmTime = outOfQuotaTime + (24 * HOUR_IN_MILLIS);
         mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .setWindow(anyInt(), eq(expectedRareAlarmTime), anyLong(),
-                        eq(TAG_QUOTA_CHECK), any(), any());
+                        eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // And back up again.
         mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .setWindow(anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
-                        eq(TAG_QUOTA_CHECK), any(), any());
+                        eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .setWindow(anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
-                        eq(TAG_QUOTA_CHECK), any(), any());
+                        eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
         mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .cancel(any(AlarmManager.OnAlarmListener.class));
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
-                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+                .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                        any(Handler.class));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
index 8e6d90c..3a9c0f0 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
@@ -107,7 +107,7 @@
         assertThat(mClientMonitor.getRequestId()).isEqualTo(id);
     }
 
-    private class TestClientMonitor extends BaseClientMonitor implements Interruptable {
+    private class TestClientMonitor extends BaseClientMonitor {
         public boolean mCanceled = false;
 
         TestClientMonitor() {
@@ -129,5 +129,10 @@
         public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) {
             mCanceled = true;
         }
+
+        @Override
+        public boolean isInterruptable() {
+            return true;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index 9e9d703..3c77a35 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -57,11 +57,16 @@
 
     public interface FakeHal {}
     public abstract static class InterruptableMonitor<T>
-            extends HalClientMonitor<T> implements  Interruptable {
+            extends HalClientMonitor<T> {
         public InterruptableMonitor() {
             super(null, null, null, null, 0, null, 0, 0,
                     mock(BiometricLogger.class), mock(BiometricContext.class));
         }
+
+        @Override
+        public boolean isInterruptable() {
+            return true;
+        }
     }
 
     @Rule
@@ -293,7 +298,6 @@
 
         assertThat(mOperation.isCanceling()).isTrue();
         verify(mClientMonitor).cancel();
-        verify(mClientMonitor, never()).cancelWithoutStarting(any());
         verify(mClientMonitor, never()).destroy();
 
         mStartedCallbackCaptor.getValue().onClientFinished(mClientMonitor, true);
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 ffacbf3..9f30c75 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
@@ -34,7 +34,6 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.withSettings;
 
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
@@ -475,8 +474,8 @@
 
     @Test
     public void testInterruptPrecedingClients_whenExpected() {
-        final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class,
-                withSettings().extraInterfaces(Interruptable.class));
+        final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class);
+        when(interruptableMonitor.isInterruptable()).thenReturn(true);
 
         final BaseClientMonitor interrupter = mock(BaseClientMonitor.class);
         when(interrupter.interruptsPrecedingClients()).thenReturn(true);
@@ -491,8 +490,8 @@
 
     @Test
     public void testDoesNotInterruptPrecedingClients_whenNotExpected() {
-        final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class,
-                withSettings().extraInterfaces(Interruptable.class));
+        final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class);
+        when(interruptableMonitor.isInterruptable()).thenReturn(true);
 
         final BaseClientMonitor interrupter = mock(BaseClientMonitor.class);
         when(interrupter.interruptsPrecedingClients()).thenReturn(false);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
index 5012335..21c9c75 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
@@ -61,7 +61,7 @@
 
     @Test
     public void noopWhenBothNull() {
-        final SensorOverlays useless = new SensorOverlays(null, null);
+        final SensorOverlays useless = new SensorOverlays(null, null, null);
         useless.show(SENSOR_ID, 2, null);
         useless.hide(SENSOR_ID);
     }
@@ -69,12 +69,12 @@
     @Test
     public void testProvidesUdfps() {
         final List<IUdfpsOverlayController> udfps = new ArrayList<>();
-        SensorOverlays sensorOverlays = new SensorOverlays(null, mSidefpsController);
+        SensorOverlays sensorOverlays = new SensorOverlays(null, mSidefpsController, null);
 
         sensorOverlays.ifUdfps(udfps::add);
         assertThat(udfps).isEmpty();
 
-        sensorOverlays = new SensorOverlays(mUdfpsOverlayController, mSidefpsController);
+        sensorOverlays = new SensorOverlays(mUdfpsOverlayController, mSidefpsController, null);
         sensorOverlays.ifUdfps(udfps::add);
         assertThat(udfps).containsExactly(mUdfpsOverlayController);
     }
@@ -96,7 +96,7 @@
 
     private void testShow(IUdfpsOverlayController udfps, ISidefpsController sidefps)
             throws Exception {
-        final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps);
+        final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps, null);
         final int reason = BiometricOverlayConstants.REASON_UNKNOWN;
         sensorOverlays.show(SENSOR_ID, reason, mAcquisitionClient);
 
@@ -126,7 +126,7 @@
 
     private void testHide(IUdfpsOverlayController udfps, ISidefpsController sidefps)
             throws Exception {
-        final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps);
+        final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps, null);
         sensorOverlays.hide(SENSOR_ID);
 
         if (udfps != null) {
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 1b5db0a..675f0e3 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
@@ -645,7 +645,7 @@
                 9 /* sensorId */, mBiometricLogger, mBiometricContext,
                 true /* isStrongBiometric */,
                 null /* taskStackListener */, mLockoutCache,
-                mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication,
+                mUdfpsOverlayController, mSideFpsController, null, allowBackgroundAuthentication,
                 mSensorProps,
                 new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock) {
             @Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
index 93cbef1..4579fc1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -118,6 +118,6 @@
         return new FingerprintDetectClient(mContext, () -> aidl, mToken,
                 6 /* requestId */, mClientMonitorCallbackConverter, 2 /* userId */,
                 "a-test", 1 /* sensorId */, mBiometricLogger, mBiometricContext,
-                mUdfpsOverlayController, true /* isStrongBiometric */);
+                mUdfpsOverlayController, null, true /* isStrongBiometric */);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 837b553..38b06c4 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -28,7 +28,6 @@
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.same;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -66,7 +65,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import java.util.ArrayList;
 import java.util.function.Consumer;
 
 @Presubmit
@@ -292,6 +290,6 @@
         mClientMonitorCallbackConverter, 0 /* userId */,
         HAT, "owner", mBiometricUtils, 8 /* sensorId */,
         mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController,
-        mSideFpsController, 6 /* maxTemplatesPerUser */, FingerprintManager.ENROLL_ENROLL);
+        mSideFpsController, null, 6 /* maxTemplatesPerUser */, FingerprintManager.ENROLL_ENROLL);
     }
 }
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 02bbe65..5fda3d6 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
@@ -891,4 +891,34 @@
         verify(mContext).startActivityAsUser(argThat(intent ->
                 intent.filterEquals(blockedAppIntent)), any(), any());
     }
+
+    @Test
+    public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() {
+        ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
+        mDeviceImpl.onVirtualDisplayCreatedLocked(
+                mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+                DISPLAY_ID);
+
+        gwpc.onRunningAppsChanged(uids);
+        mDeviceImpl.onRunningAppsChanged(uids);
+
+        assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(1);
+        verify(mRunningAppsChangedCallback).accept(new ArraySet<>(Arrays.asList(UID_1, UID_2)));
+    }
+
+    @Test
+    public void noRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() {
+        ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
+        mDeviceImpl.onVirtualDisplayCreatedLocked(
+                mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+                DISPLAY_ID);
+        mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID);
+
+        // This call should not throw any exceptions.
+        gwpc.onRunningAppsChanged(uids);
+
+        assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 1b867be..8f6bee1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -92,7 +92,6 @@
 
         HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
         mLocalDeviceTypes.add(HdmiDeviceInfo.DEVICE_PLAYBACK);
-        mLocalDeviceTypes.add(DEVICE_AUDIO_SYSTEM);
 
         mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, mLocalDeviceTypes,
                 new FakeAudioDeviceVolumeManagerWrapper()));
@@ -480,6 +479,7 @@
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
         mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+        mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveFeatures(Constants.ADDR_TV,
@@ -501,6 +501,7 @@
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
         mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+        mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage reportFeatures = ReportFeaturesMessage.build(
@@ -517,6 +518,7 @@
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
         mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+        mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage reportFeatures = ReportFeaturesMessage.build(
diff --git a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
index c68db34..6590a2b 100644
--- a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
@@ -36,6 +36,7 @@
 import com.android.server.input.BatteryController.POLLING_PERIOD_MILLIS
 import com.android.server.input.BatteryController.UEventManager
 import com.android.server.input.BatteryController.UEventManager.UEventBatteryListener
+import com.android.server.input.BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS
 import org.hamcrest.Description
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
@@ -528,10 +529,109 @@
             matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f))
 
         // The battery is no longer present after the timeout expires.
-        testLooper.moveTimeForward(BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS - 1)
+        testLooper.moveTimeForward(USI_BATTERY_VALIDITY_DURATION_MILLIS - 1)
         testLooper.dispatchNext()
         listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID), times(2))
         assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
             isInvalidBatteryState(USI_DEVICE_ID))
     }
+
+    @Test
+    fun testStylusPresenceExtendsValidUsiBatteryState() {
+        `when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
+        `when`(native.getBatteryStatus(USI_DEVICE_ID)).thenReturn(STATUS_DISCHARGING)
+        `when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(78)
+
+        addInputDevice(USI_DEVICE_ID, supportsUsi = true)
+        testLooper.dispatchNext()
+        val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+        verify(uEventManager)
+            .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+
+        // There is a UEvent signaling a battery change. The battery state is now valid.
+        uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
+        val listener = createMockListener()
+        batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+        listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f)
+        assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+            matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+
+        // Stylus presence is detected before the validity timeout expires.
+        testLooper.moveTimeForward(100)
+        testLooper.dispatchAll()
+        batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, TIMESTAMP)
+
+        // Ensure that timeout was extended, and the battery state is now valid for longer.
+        testLooper.moveTimeForward(USI_BATTERY_VALIDITY_DURATION_MILLIS - 100)
+        testLooper.dispatchAll()
+        assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+            matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+
+        // Ensure the validity period expires after the expected amount of time.
+        testLooper.moveTimeForward(100)
+        testLooper.dispatchNext()
+        listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
+        assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+            isInvalidBatteryState(USI_DEVICE_ID))
+    }
+
+    @Test
+    fun testStylusPresenceMakesUsiBatteryStateValid() {
+        `when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
+        `when`(native.getBatteryStatus(USI_DEVICE_ID)).thenReturn(STATUS_DISCHARGING)
+        `when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(78)
+
+        addInputDevice(USI_DEVICE_ID, supportsUsi = true)
+        testLooper.dispatchNext()
+        val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+        verify(uEventManager)
+            .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+
+        // The USI battery state is initially invalid.
+        val listener = createMockListener()
+        batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+        listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
+        assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+            isInvalidBatteryState(USI_DEVICE_ID))
+
+        // A stylus presence is detected. This validates the battery state.
+        batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, TIMESTAMP)
+
+        listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f)
+        assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+            matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+    }
+
+    @Test
+    fun testStylusPresenceDoesNotMakeUsiBatteryStateValidAtBoot() {
+        `when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
+        // At boot, the USI device always reports a capacity value of 0.
+        `when`(native.getBatteryStatus(USI_DEVICE_ID)).thenReturn(STATUS_UNKNOWN)
+        `when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(0)
+
+        addInputDevice(USI_DEVICE_ID, supportsUsi = true)
+        testLooper.dispatchNext()
+        val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+        verify(uEventManager)
+            .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+
+        // The USI battery state is initially invalid.
+        val listener = createMockListener()
+        batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+        listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
+        assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+            isInvalidBatteryState(USI_DEVICE_ID))
+
+        // Since the capacity reported is 0, stylus presence does not validate the battery state.
+        batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, TIMESTAMP)
+
+        assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+            isInvalidBatteryState(USI_DEVICE_ID))
+
+        // However, if a UEvent reports a battery capacity of 0, the battery state is now valid.
+        uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
+        listener.verifyNotified(USI_DEVICE_ID, status = STATUS_UNKNOWN, capacity = 0f)
+        assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+            matchesState(USI_DEVICE_ID, status = STATUS_UNKNOWN, capacity = 0f))
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index a5fedef..21d2784 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -36,7 +36,6 @@
 
 import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
 
-import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -59,30 +58,6 @@
     private static final String KEY_MIN_BGUSER_IMPORTANT = "concurrency_min_bguser_important_test";
     private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
 
-    @After
-    public void tearDown() throws Exception {
-        resetConfig();
-    }
-
-    private void resetConfig() {
-        // DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_FGS, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
-                KEY_MAX_BGUSER_IMPORTANT, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_FGS, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
-                KEY_MIN_BGUSER_IMPORTANT, null, false);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, null, false);
-    }
-
     private void check(@Nullable DeviceConfig.Properties config,
             int defaultTotal,
             @NonNull List<Pair<Integer, Integer>> defaultMin,
@@ -90,10 +65,6 @@
             boolean expectedValid, int expectedTotal,
             @NonNull List<Pair<Integer, Integer>> expectedMinLimits,
             @NonNull List<Pair<Integer, Integer>> expectedMaxLimits) throws Exception {
-        resetConfig();
-        if (config != null) {
-            DeviceConfig.setProperties(config);
-        }
 
         final WorkTypeConfig counts;
         try {
@@ -112,7 +83,9 @@
             }
         }
 
-        counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
+        if (config != null) {
+            counts.update(config);
+        }
 
         assertEquals(expectedTotal, counts.getMaxTotal());
         for (Pair<Integer, Integer> min : expectedMinLimits) {
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
index dbcd38c..dc9f907 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
@@ -229,6 +229,29 @@
         }
     }
 
+    @Test(expected = SecurityException.class)
+    public void testGetApplicationLocales_currentImeQueryNonForegroundAppLocales_fails()
+            throws Exception {
+        doReturn(DEFAULT_UID).when(mMockPackageManager)
+                .getPackageUidAsUser(anyString(), any(), anyInt());
+        doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES))
+                .when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt());
+        String imPkgName = getCurrentInputMethodPackageName();
+        doReturn(Binder.getCallingUid()).when(mMockPackageManager)
+                .getPackageUidAsUser(eq(imPkgName), any(), anyInt());
+        doReturn(false).when(mMockActivityManager).isAppForeground(anyInt());
+        setUpFailingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES);
+
+        try {
+            mLocaleManagerService.getApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+            fail("Expected SecurityException");
+        } finally {
+            verify(mMockContext).enforceCallingOrSelfPermission(
+                    eq(android.Manifest.permission.READ_APP_SPECIFIC_LOCALES),
+                    anyString());
+        }
+    }
+
     @Test
     public void testGetApplicationLocales_appSpecificConfigAbsent_returnsEmptyList()
             throws Exception {
@@ -307,7 +330,7 @@
     }
 
     @Test
-    public void testGetApplicationLocales_callerIsCurrentInputMethod_returnsLocales()
+    public void testGetApplicationLocales_currentImeQueryForegroundAppLocales_returnsLocales()
             throws Exception {
         doReturn(DEFAULT_UID).when(mMockPackageManager)
                 .getPackageUidAsUser(anyString(), any(), anyInt());
@@ -316,6 +339,7 @@
         String imPkgName = getCurrentInputMethodPackageName();
         doReturn(Binder.getCallingUid()).when(mMockPackageManager)
                 .getPackageUidAsUser(eq(imPkgName), any(), anyInt());
+        doReturn(true).when(mMockActivityManager).isAppForeground(anyInt());
 
         LocaleList locales =
                 mLocaleManagerService.getApplicationLocales(
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
index c321639..1a8ef9e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
@@ -387,7 +387,7 @@
 
         // delete user
         when(mSnapshot.getUserInfos()).thenReturn(USER_INFO_LIST);
-        appsFilter.onUserDeleted(ADDED_USER);
+        appsFilter.onUserDeleted(mSnapshot, ADDED_USER);
 
         for (int subjectUserId : USER_ARRAY) {
             for (int otherUserId : USER_ARRAY) {
@@ -925,7 +925,7 @@
         assertTrue(appsFilter.shouldFilterApplication(mSnapshot, DUMMY_OVERLAY_APPID,
                 overlaySetting, actorSetting, SYSTEM_USER));
 
-        appsFilter.removePackage(mSnapshot, targetSetting, false /* isReplace */);
+        appsFilter.removePackage(mSnapshot, targetSetting);
 
         // Actor loses visibility to the overlay via removal of the target
         assertTrue(appsFilter.shouldFilterApplication(mSnapshot, DUMMY_ACTOR_APPID, actorSetting,
@@ -1267,7 +1267,7 @@
         watcher.verifyNoChangeReported("get");
 
         // remove a package
-        appsFilter.removePackage(mSnapshot, seesNothing, false /* isReplace */);
+        appsFilter.removePackage(mSnapshot, seesNothing);
         watcher.verifyChangeReported("removePackage");
     }
 
@@ -1337,7 +1337,7 @@
                 target.getPackageName()));
 
         // New changes don't affect the snapshot
-        appsFilter.removePackage(mSnapshot, target, false);
+        appsFilter.removePackage(mSnapshot, target);
         assertTrue(
                 appsFilter.shouldFilterApplication(mSnapshot, DUMMY_CALLING_APPID, instrumentation,
                         target,
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
index 7731a32..c2556e9 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
@@ -44,7 +44,9 @@
 public class CpuWakeupStatsTest {
     private static final String KERNEL_REASON_ALARM_IRQ = "120 test.alarm.device";
     private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
-    private static final String KERNEL_REASON_UNKNOWN = "unsupported-free-form-reason";
+    private static final String KERNEL_REASON_UNKNOWN = "free-form-reason test.alarm.device";
+    private static final String KERNEL_REASON_UNSUPPORTED = "-1 test.alarm.device";
+    private static final String KERNEL_REASON_ABORT = "Abort: due to test.alarm.device";
 
     private static final int TEST_UID_1 = 13239823;
     private static final int TEST_UID_2 = 25268423;
@@ -57,6 +59,7 @@
 
     @Test
     public void removesOldWakeups() {
+        // The xml resource doesn't matter for this test.
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_1);
 
         final Set<Long> timestamps = new HashSet<>();
@@ -165,11 +168,36 @@
 
         obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN);
 
-        // Unrelated subsystems, should be ignored.
+        // Should be ignored as this type of wakeup is unsupported.
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
 
         // There should be nothing in the attribution map.
         assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
     }
+
+    @Test
+    public void unsupportedAttribution() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+
+        long wakeupTime = 970934;
+        obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNSUPPORTED);
+
+        // Should be ignored as this type of wakeup is unsupported.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
+
+        // There should be nothing in the attribution map.
+        assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
+
+        wakeupTime = 883124;
+        obj.noteWakeupTimeAndReason(wakeupTime, 3, KERNEL_REASON_ABORT);
+
+        // Should be ignored as this type of wakeup is unsupported.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 2, TEST_UID_1, TEST_UID_4);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 5, TEST_UID_3);
+
+        // There should be nothing in the attribution map.
+        assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java b/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
index 0b27f87..4762696 100644
--- a/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
@@ -125,7 +125,7 @@
         @Test
         public void testThatLoggingWorksAsExpected() {
             for (EventLogger.Event event: mEventsToInsert) {
-                mEventLogger.log(event);
+                mEventLogger.enqueue(event);
             }
 
             mEventLogger.dump(mTestPrintWriter);
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index a917c57..6ef5b0e 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -51,6 +51,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -87,6 +88,7 @@
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
     }
 
+    @Ignore("b/253871109")
     @Test
     public void testAddPinCreatesPinned() throws RemoteException {
         grantSlicePermission();
@@ -97,6 +99,7 @@
         verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString());
     }
 
+    @Ignore("b/253871109")
     @Test
     public void testRemovePinDestroysPinned() throws RemoteException {
         grantSlicePermission();
@@ -109,6 +112,7 @@
         verify(mCreatedSliceState, never()).destroy();
     }
 
+    @Ignore("b/253871109")
     @Test
     public void testCheckAutoGrantPermissions() throws RemoteException {
         String[] testPerms = new String[] {
@@ -128,12 +132,14 @@
         verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
     }
 
+    @Ignore("b/253871109")
     @Test(expected = IllegalStateException.class)
     public void testNoPinThrow() throws Exception {
         grantSlicePermission();
         mService.getPinnedSpecs(TEST_URI, "pkg");
     }
 
+    @Ignore("b/253871109")
     @Test
     public void testGetPinnedSpecs() throws Exception {
         grantSlicePermission();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d314a65..2c7867c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1147,8 +1147,12 @@
             "carrier_data_call_apn_retry_after_disconnect_long";
 
     /**
-     * Data call setup permanent failure causes by the carrier
+     * Data call setup permanent failure causes by the carrier.
+     *
+     * @deprecated This API key was added in mistake and is not used anymore by the telephony data
+     * frameworks.
      */
+    @Deprecated
     public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS =
             "carrier_data_call_permanent_failure_strings";
 
@@ -2103,6 +2107,16 @@
      * is immediately closed (disabling keep-alive).
      */
     public static final String KEY_MMS_CLOSE_CONNECTION_BOOL = "mmsCloseConnection";
+    /**
+     * Waiting time in milliseconds used before releasing an MMS data call. Not tearing down an MMS
+     * data connection immediately helps to reduce the message delivering latency if messaging
+     * continues between all parties in the conversation since the same data connection can be
+     * reused for further messages.
+     *
+     * This timer will control how long the data call will be kept alive before being torn down.
+     */
+    public static final String KEY_MMS_NETWORK_RELEASE_TIMEOUT_MILLIS_INT =
+            "mms_network_release_timeout_millis_int";
 
     /**
      * The flatten {@link android.content.ComponentName componentName} of the activity that can
@@ -8436,7 +8450,8 @@
      *
      * The syntax of the retry rule:
      * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network capabilities
-     *    are supported.
+     *    are supported. If the capabilities are not specified, then the retry rule only applies
+     *    to the current failed APN used in setup data call request.
      * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
      *
      * 2. Retry based on {@link DataFailCause}
@@ -8447,15 +8462,16 @@
      * "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...],
      *     [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
      *
+     * 4. Permanent fail causes (no timer-based retry) on the current failed APN. Retry interval
+     *    is specified for retrying the next available APN.
+     * "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|65543|65547|
+     *     2252|2253|2254, retry_interval=2500"
+     *
      * For example,
      * "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached
      * network request is emergency, then retry data network setup every 1 second for up to 20
      * times.
      *
-     * "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|2254
-     * , maximum_retries=0" means for those fail causes, never retry with timers. Note that
-     * when environment changes, retry can still happen.
-     *
      * "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
      * "5000|10000|15000|20000|40000|60000|120000|240000|600000|1200000|1800000"
      * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
@@ -9069,6 +9085,7 @@
         sDefaults.putInt(KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT, -1);
         sDefaults.putInt(KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT, -1);
         sDefaults.putInt(KEY_MMS_SUBJECT_MAX_LENGTH_INT, 40);
+        sDefaults.putInt(KEY_MMS_NETWORK_RELEASE_TIMEOUT_MILLIS_INT, 5 * 1000);
         sDefaults.putString(KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING, "");
         sDefaults.putString(KEY_MMS_HTTP_PARAMS_STRING, "");
         sDefaults.putString(KEY_MMS_NAI_SUFFIX_STRING, "");
@@ -9432,8 +9449,13 @@
         sDefaults.putStringArray(
                 KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY, new String[] {
                         "capabilities=eims, retry_interval=1000, maximum_retries=20",
-                        "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2252|"
-                                + "2253|2254, maximum_retries=0", // No retry for those causes
+                        // Permanent fail causes. When setup data call fails with the following
+                        // fail causes, telephony data frameworks will stop timer-based retry on
+                        // the failed APN until power cycle, APM, or some special events. Note that
+                        // even timer-based retry is not performed, condition-based (RAT changes,
+                        // registration state changes) retry can still happen.
+                        "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|"
+                                + "-3|65543|65547|2252|2253|2254, retry_interval=2500",
                         "capabilities=mms|supl|cbs, retry_interval=2000",
                         "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
                                 + "5000|10000|15000|20000|40000|60000|120000|240000|"
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index d670e55..1f301c1 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2268,7 +2268,21 @@
             RESULT_RIL_SIM_ABSENT,
             RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED,
             RESULT_RIL_ACCESS_BARRED,
-            RESULT_RIL_BLOCKED_DUE_TO_CALL
+            RESULT_RIL_BLOCKED_DUE_TO_CALL,
+            RESULT_RIL_GENERIC_ERROR,
+            RESULT_RIL_INVALID_RESPONSE,
+            RESULT_RIL_SIM_PIN2,
+            RESULT_RIL_SIM_PUK2,
+            RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE,
+            RESULT_RIL_SIM_ERROR,
+            RESULT_RIL_INVALID_SIM_STATE,
+            RESULT_RIL_NO_SMS_TO_ACK,
+            RESULT_RIL_SIM_BUSY,
+            RESULT_RIL_SIM_FULL,
+            RESULT_RIL_NO_SUBSCRIPTION,
+            RESULT_RIL_NO_NETWORK_FOUND,
+            RESULT_RIL_DEVICE_IN_USE,
+            RESULT_RIL_ABORTED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Result {}
@@ -2534,7 +2548,7 @@
     public static final int RESULT_RIL_SIM_ABSENT = 120;
 
     /**
-     * 1X voice and SMS are not allowed simulteneously.
+     * 1X voice and SMS are not allowed simultaneously.
      */
     public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121;
 
@@ -2553,6 +2567,73 @@
      */
     public static final int RESULT_RIL_GENERIC_ERROR = 124;
 
+    /**
+     * A RIL internal error when one of the RIL layers receives an unrecognized response from a
+     * lower layer.
+     */
+    public static final int RESULT_RIL_INVALID_RESPONSE = 125;
+
+    /**
+     * Operation requires SIM PIN2 to be entered
+     */
+    public static final int RESULT_RIL_SIM_PIN2 = 126;
+
+    /**
+     * Operation requires SIM PUK2 to be entered
+     */
+    public static final int RESULT_RIL_SIM_PUK2 = 127;
+
+    /**
+     * Fail to find CDMA subscription from specified location
+     */
+    public static final int RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE = 128;
+
+    /**
+     * Received error from SIM card
+     */
+    public static final int RESULT_RIL_SIM_ERROR = 129;
+
+    /**
+     * Cannot process the request in current SIM state
+     */
+    public static final int RESULT_RIL_INVALID_SIM_STATE = 130;
+
+    /**
+     * ACK received when there is no SMS to ack
+     */
+    public static final int RESULT_RIL_NO_SMS_TO_ACK = 131;
+
+    /**
+     * SIM is busy
+     */
+    public static final int RESULT_RIL_SIM_BUSY = 132;
+
+    /**
+     * The target EF is full
+     */
+    public static final int RESULT_RIL_SIM_FULL = 133;
+
+    /**
+     * Device does not have subscription
+     */
+    public static final int RESULT_RIL_NO_SUBSCRIPTION = 134;
+
+    /**
+     * Network cannot be found
+     */
+    public static final int RESULT_RIL_NO_NETWORK_FOUND = 135;
+
+    /**
+     * Operation cannot be performed because the device is currently in use
+     */
+    public static final int RESULT_RIL_DEVICE_IN_USE = 136;
+
+    /**
+     * Operation aborted
+     */
+    public static final int RESULT_RIL_ABORTED = 137;
+
+
     // SMS receiving results sent as a "result" extra in {@link Intents.SMS_REJECTED_ACTION}
 
     /**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 439eaa6..ef693b5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3942,6 +3942,10 @@
      * may provide one. Or, a carrier may decide to provide the phone number via source
      * {@link #PHONE_NUMBER_SOURCE_CARRIER carrier} if neither source UICC nor IMS is available.
      *
+     * <p>The availability and correctness of the phone number depends on the underlying source
+     * and the network etc. Additional verification is needed to use this number for
+     * security-related or other sensitive scenarios.
+     *
      * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
      *                       for the default one.
      * @param source the source of the phone number, one of the PHONE_NUMBER_SOURCE_* constants.
@@ -4175,18 +4179,18 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
-    public void setUserHandle(int subscriptionId, @Nullable UserHandle userHandle) {
+    public void setSubscriptionUserHandle(int subscriptionId, @Nullable UserHandle userHandle) {
         if (!isValidSubscriptionId(subscriptionId)) {
-            throw new IllegalArgumentException("[setUserHandle]: Invalid subscriptionId: "
-                    + subscriptionId);
+            throw new IllegalArgumentException("[setSubscriptionUserHandle]: "
+                    + "Invalid subscriptionId: " + subscriptionId);
         }
 
         try {
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
-                iSub.setUserHandle(userHandle, subscriptionId, mContext.getOpPackageName());
+                iSub.setSubscriptionUserHandle(userHandle, subscriptionId);
             } else {
-                throw new IllegalStateException("[setUserHandle]: "
+                throw new IllegalStateException("[setSubscriptionUserHandle]: "
                         + "subscription service unavailable");
             }
         } catch (RemoteException ex) {
@@ -4211,18 +4215,18 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
-    public @Nullable UserHandle getUserHandle(int subscriptionId) {
+    public @Nullable UserHandle getSubscriptionUserHandle(int subscriptionId) {
         if (!isValidSubscriptionId(subscriptionId)) {
-            throw new IllegalArgumentException("[getUserHandle]: Invalid subscriptionId: "
-                    + subscriptionId);
+            throw new IllegalArgumentException("[getSubscriptionUserHandle]: "
+                    + "Invalid subscriptionId: " + subscriptionId);
         }
 
         try {
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
-                return iSub.getUserHandle(subscriptionId, mContext.getOpPackageName());
+                return iSub.getSubscriptionUserHandle(subscriptionId);
             } else {
-                throw new IllegalStateException("[getUserHandle]: "
+                throw new IllegalStateException("[getSubscriptionUserHandle]: "
                         + "subscription service unavailable");
             }
         } catch (RemoteException ex) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9ecebf1..5f1d086 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3323,15 +3323,14 @@
             case NETWORK_TYPE_TD_SCDMA:
                 return NETWORK_TYPE_BITMASK_TD_SCDMA;
             case NETWORK_TYPE_LTE:
-                return NETWORK_TYPE_BITMASK_LTE;
             case NETWORK_TYPE_LTE_CA:
-                return NETWORK_TYPE_BITMASK_LTE_CA;
+                return NETWORK_TYPE_BITMASK_LTE;
             case NETWORK_TYPE_NR:
                 return NETWORK_TYPE_BITMASK_NR;
             case NETWORK_TYPE_IWLAN:
                 return NETWORK_TYPE_BITMASK_IWLAN;
             case NETWORK_TYPE_IDEN:
-                return (1 << (NETWORK_TYPE_IDEN - 1));
+                return NETWORK_TYPE_BITMASK_IDEN;
             default:
                 return NETWORK_TYPE_BITMASK_UNKNOWN;
         }
@@ -9477,7 +9476,8 @@
             ALLOWED_NETWORK_TYPES_REASON_USER,
             ALLOWED_NETWORK_TYPES_REASON_POWER,
             ALLOWED_NETWORK_TYPES_REASON_CARRIER,
-            ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
+            ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G,
+            ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AllowedNetworkTypesReason {
@@ -9516,6 +9516,15 @@
     public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3;
 
     /**
+     * To indicate allowed network type change is requested by an update to the
+     * {@link android.os.UserManager.DISALLOW_CELLULAR_2G} user restriction.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS = 4;
+
+    /**
      * Set the allowed network types of the device and provide the reason triggering the allowed
      * network change.
      * <p>Requires permission: android.Manifest.MODIFY_PHONE_STATE or
@@ -9607,6 +9616,7 @@
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER:
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER:
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
+            case ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS:
                 return true;
         }
         return false;
@@ -13911,7 +13921,8 @@
                     NETWORK_TYPE_BITMASK_LTE,
                     NETWORK_TYPE_BITMASK_LTE_CA,
                     NETWORK_TYPE_BITMASK_NR,
-                    NETWORK_TYPE_BITMASK_IWLAN
+                    NETWORK_TYPE_BITMASK_IWLAN,
+                    NETWORK_TYPE_BITMASK_IDEN
             })
     public @interface NetworkTypeBitMask {}
 
@@ -13971,6 +13982,10 @@
      */
     public static final long NETWORK_TYPE_BITMASK_HSPA = (1 << (NETWORK_TYPE_HSPA -1));
     /**
+     * network type bitmask indicating the support of radio tech iDen.
+     */
+    public static final long NETWORK_TYPE_BITMASK_IDEN = (1 << (NETWORK_TYPE_IDEN - 1));
+    /**
      * network type bitmask indicating the support of radio tech HSPAP.
      */
     public static final long NETWORK_TYPE_BITMASK_HSPAP = (1 << (NETWORK_TYPE_HSPAP -1));
@@ -13988,12 +14003,13 @@
      */
     public static final long NETWORK_TYPE_BITMASK_LTE = (1 << (NETWORK_TYPE_LTE -1));
     /**
-     * NOT USED; this bitmask is exposed accidentally, will be deprecated in U.
+     * NOT USED; this bitmask is exposed accidentally.
      * If used, will be converted to {@link #NETWORK_TYPE_BITMASK_LTE}.
      * network type bitmask indicating the support of radio tech LTE CA (carrier aggregation).
      *
-     * @see #NETWORK_TYPE_BITMASK_LTE
+     * @deprecated Please use {@link #NETWORK_TYPE_BITMASK_LTE} instead.
      */
+    @Deprecated
     public static final long NETWORK_TYPE_BITMASK_LTE_CA = (1 << (NETWORK_TYPE_LTE_CA -1));
 
     /**
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index f0401534..a3cbb4a 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -204,6 +204,7 @@
 
     private IImsServiceControllerListener mListener;
     private final Object mListenerLock = new Object();
+    private final Object mExecutorLock = new Object();
     private Executor mExecutor;
 
     /**
@@ -214,10 +215,6 @@
      * vendor use Runnable::run.
      */
     public ImsService() {
-        mExecutor = ImsService.this.getExecutor();
-        if (mExecutor == null) {
-            mExecutor = Runnable::run;
-        }
     }
 
     /**
@@ -356,7 +353,7 @@
                 ImsConfigImplBase c =
                         ImsService.this.getConfigForSubscription(slotId, subId);
                 if (c != null) {
-                    c.setDefaultExecutor(mExecutor);
+                    c.setDefaultExecutor(getCachedExecutor());
                     return c.getIImsConfig();
                 } else {
                     return null;
@@ -370,7 +367,7 @@
                 ImsRegistrationImplBase r =
                         ImsService.this.getRegistrationForSubscription(slotId, subId);
                 if (r != null) {
-                    r.setDefaultExecutor(mExecutor);
+                    r.setDefaultExecutor(getCachedExecutor());
                     return r.getBinder();
                 } else {
                     return null;
@@ -383,7 +380,7 @@
             return executeMethodAsyncForResult(() -> {
                 SipTransportImplBase s =  ImsService.this.getSipTransport(slotId);
                 if (s != null) {
-                    s.setDefaultExecutor(mExecutor);
+                    s.setDefaultExecutor(getCachedExecutor());
                     return s.getBinder();
                 } else {
                     return null;
@@ -427,11 +424,21 @@
         return null;
     }
 
+    private Executor getCachedExecutor() {
+        synchronized (mExecutorLock) {
+            if (mExecutor == null) {
+                Executor e = ImsService.this.getExecutor();
+                mExecutor = (e != null) ? e : Runnable::run;
+            }
+            return mExecutor;
+        }
+    }
+
     private IImsMmTelFeature createMmTelFeatureInternal(int slotId, int subscriptionId) {
         MmTelFeature f = createMmTelFeatureForSubscription(slotId, subscriptionId);
         if (f != null) {
             setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
-            f.setDefaultExecutor(mExecutor);
+            f.setDefaultExecutor(getCachedExecutor());
             return f.getBinder();
         } else {
             Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
@@ -443,7 +450,7 @@
         MmTelFeature f = createEmergencyOnlyMmTelFeature(slotId);
         if (f != null) {
             setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
-            f.setDefaultExecutor(mExecutor);
+            f.setDefaultExecutor(getCachedExecutor());
             return f.getBinder();
         } else {
             Log.e(LOG_TAG, "createEmergencyOnlyMmTelFeatureInternal: null feature returned.");
@@ -451,10 +458,10 @@
         }
     }
 
-    private IImsRcsFeature createRcsFeatureInternal(int slotId, int subI) {
-        RcsFeature f = createRcsFeatureForSubscription(slotId, subI);
+    private IImsRcsFeature createRcsFeatureInternal(int slotId, int subId) {
+        RcsFeature f = createRcsFeatureForSubscription(slotId, subId);
         if (f != null) {
-            f.setDefaultExecutor(mExecutor);
+            f.setDefaultExecutor(getCachedExecutor());
             setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
             return f.getBinder();
         } else {
@@ -609,7 +616,8 @@
     private void executeMethodAsync(Runnable r, String errorLogName) {
         try {
             CompletableFuture.runAsync(
-                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r),
+                    getCachedExecutor()).join();
         } catch (CancellationException | CompletionException e) {
             Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
                     + e.getMessage());
@@ -618,7 +626,7 @@
 
     private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) {
         CompletableFuture<T> future = CompletableFuture.supplyAsync(
-                () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+                () -> TelephonyUtils.runWithCleanCallingIdentity(r), getCachedExecutor());
         try {
             return future.get();
         } catch (ExecutionException | InterruptedException e) {
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index f5b158f..a42327b 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -394,10 +394,12 @@
     @VisibleForTesting
     public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
         try {
-            // If we have just connected, send queued status.
-            c.notifyImsFeatureStatus(getFeatureState());
-            // Add the callback if the callback completes successfully without a RemoteException.
-            mStatusCallbacks.register(c);
+            synchronized (mStatusCallbacks) {
+                // Add the callback if the callback completes successfully without a RemoteException
+                mStatusCallbacks.register(c);
+                // If we have just connected, send queued status.
+                c.notifyImsFeatureStatus(getFeatureState());
+            }
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
         }
@@ -409,7 +411,9 @@
      */
     @VisibleForTesting
     public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
-        mStatusCallbacks.unregister(c);
+        synchronized (mStatusCallbacks) {
+            mStatusCallbacks.unregister(c);
+        }
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 0211a7f..4752cca 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -323,22 +323,20 @@
       *
       * @param userHandle the user handle for this subscription
       * @param subId the unique SubscriptionInfo index in database
-      * @param callingPackage The package making the IPC.
       *
       * @throws SecurityException if doesn't have MANAGE_SUBSCRIPTION_USER_ASSOCIATION
       * @throws IllegalArgumentException if subId is invalid.
       */
-    int setUserHandle(in UserHandle userHandle, int subId, String callingPackage);
+    int setSubscriptionUserHandle(in UserHandle userHandle, int subId);
 
     /**
      * Get UserHandle for this subscription
      *
      * @param subId the unique SubscriptionInfo index in database
-     * @param callingPackage the package making the IPC
      * @return userHandle associated with this subscription.
      *
-     * @throws SecurityException if doesn't have SMANAGE_SUBSCRIPTION_USER_ASSOCIATION
+     * @throws SecurityException if doesn't have MANAGE_SUBSCRIPTION_USER_ASSOCIATION
      * @throws IllegalArgumentException if subId is invalid.
      */
-     UserHandle getUserHandle(int subId, String callingPackage);
+     UserHandle getSubscriptionUserHandle(int subId);
 }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 9892671..9f612e6 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -536,6 +536,12 @@
     int RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN = 230;
     int RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN = 231;
     int RIL_REQUEST_EXIT_EMERGENCY_MODE = 232;
+    int RIL_REQUEST_SET_SRVCC_CALL_INFO = 233;
+    int RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO = 234;
+    int RIL_REQUEST_START_IMS_TRAFFIC = 235;
+    int RIL_REQUEST_STOP_IMS_TRAFFIC = 236;
+    int RIL_REQUEST_SEND_ANBR_QUERY = 237;
+    int RIL_REQUEST_TRIGGER_EPS_FALLBACK = 238;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -607,4 +613,7 @@
     int RIL_UNSOL_REGISTRATION_FAILED = 1104;
     int RIL_UNSOL_BARRING_INFO_CHANGED = 1105;
     int RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT = 1106;
+    int RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION = 1107;
+    int RIL_UNSOL_CONNECTION_SETUP_FAILURE = 1108;
+    int RIL_UNSOL_NOTIFY_ANBR = 1109;
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 16753e6..ca5b2af 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -24,6 +24,8 @@
 import androidx.test.uiautomator.Until
 import com.android.server.wm.flicker.testapp.ActivityOptions
 import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.Condition
+import com.android.server.wm.traces.common.DeviceStateDump
 import com.android.server.wm.traces.parser.toFlickerComponent
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import java.util.regex.Pattern
@@ -47,9 +49,10 @@
         wmHelper: WindowManagerStateHelper,
         expectedWindowName: String,
         action: String?,
-        stringExtras: Map<String, String>
+        stringExtras: Map<String, String>,
+        waitConditions: Array<Condition<DeviceStateDump>>
     ) {
-        super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
+        super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras, waitConditions)
         waitIMEShown(wmHelper)
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest_ShellTransit.kt
index 0837c00..5686965 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest_ShellTransit.kt
@@ -44,7 +44,7 @@
     OpenAppAfterCameraTest(testSpec) {
     @Before
     override fun before() {
-        Assume.assumeFalse(isShellTransitionsEnabled)
+        Assume.assumeTrue(isShellTransitionsEnabled)
     }
 
     @FlakyTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index 73e6d22..5e6fc21 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -19,11 +19,11 @@
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.RequiresDevice
 import android.view.Surface
+import android.view.WindowManagerPolicyConstants
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -68,7 +68,6 @@
                     tapl.setExpectedRotation(Surface.ROTATION_0)
                 }
                 RemoveAllTasksButHomeRule.removeAllTasksButHome()
-                this.setRotation(testSpec.startRotation)
             }
             transitions {
                 tapl
@@ -187,7 +186,13 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
-            return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests()
+            return FlickerTestParameterFactory.getInstance()
+                // TAPL fails on landscape mode b/240916028
+                .getConfigNonRotationTests(
+                    supportedNavigationModes = listOf(
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY
+                    )
+                )
         }
     }
 }